如何为内插字符串设置模拟?
How to setup a mock for an interpolated string?
假设我有一个记录器接口实现,上面有一个 Trace 方法,例如:
public interface IMyLogger
{
void Trace(string message, params object[] parameters);
}
public class MyLogger : IMyLogger
{
public void Trace(string message, params object[] parameters)
{
// Writes the trace to a log file somewhere
}
}
以及使用此记录器调用的方法调用和内插字符串,如下所示:
public class MyWorker
{
private IMyLogger Logger { get; set; }
public MyWorker(IMyLogger logger)
{
Logger = logger;
}
public void DoSomeWork(int x)
{
Logger.Trace($"Value of x is {x}");
}
}
我正在为 DoSomeWork 方法编写单元测试。这是我现在的,测试通过:
[TestMethod]
public void DoSomeWork_ShouldLogTrace()
{
var mockLogger = new Mock<IMyLogger>(MockBehavior.Strict);
mockLogger.Setup(l => l.Trace("Value of x is 5", It.IsAny<object[]>());
var testWorker = new MyWorker();
testWorker.DoSomeWork(5);
}
我的问题是:有没有一种方法可以将内插字符串而不是字符串内插结果传递给 Trace 设置?
is there a way I can pass the interpolated string to the Trace setup instead of the result of the string interpolation?
没有。 C# 内插字符串不是模板引擎,而是 compile-time 功能。
这一行
Logger.Trace($"Value of x is {x}");
将使用格式化消息调用 Trace
方法,不带参数。
因此,要么使用经典占位符,要么使用记录器引擎,它处理模板参数,例如 Serilog。
您不能传递内插字符串,但您可以验证 Trace
方法是否使用您期望的值调用。
[TestMethod]
public void DoSomeWork_ShouldLogWarning()
{
var mockLogger = new Mock<IMyLogger>(MockBehavior.Strict);
mockLogger.Setup(l => l.Trace(It.IsAny<string>(), It.IsAny<object[]>()));
var testWorker = new MyWorker(mockLogger.Object);
const int expectedValue = 5;
testWorker.DoSomeWork(expectedValue);
mockLogger.Verify(x => x.Trace($"Value of x is {expectedValue}"));
}
在命名空间 System
中使用 FormattableString
type。
也就是说,如果你可以重载:
void Trace(FormattableString formattableMessage);
并确保使用它,然后像这样调用:
Logger.Trace($"Value of x is {x}");
将生成一个 "rich" 对象,您可以在其中检查 .Format
和 .GetArguments
等
解释是 compile-time 表达式 $" ... "
是 隐式转换 到特殊类型 FormattableString
而不是 "interpolate" 马上,而是跟踪格式和每个参数。在实施 // Writes the trace to a log file somewhere
中,对这个富对象使用 .ToString()
以执行实际的插值。
那么最小起订量设置将类似于:
// using nice Strict mock
mockLogger.Setup(l => l.Trace(It.Is(
(FormattableString x) => x.Format == "Value of x is {0}"
)));
已编辑:必须在模拟设置中使用 {0}
和索引 0
,而不是 {x}
。
假设我有一个记录器接口实现,上面有一个 Trace 方法,例如:
public interface IMyLogger
{
void Trace(string message, params object[] parameters);
}
public class MyLogger : IMyLogger
{
public void Trace(string message, params object[] parameters)
{
// Writes the trace to a log file somewhere
}
}
以及使用此记录器调用的方法调用和内插字符串,如下所示:
public class MyWorker
{
private IMyLogger Logger { get; set; }
public MyWorker(IMyLogger logger)
{
Logger = logger;
}
public void DoSomeWork(int x)
{
Logger.Trace($"Value of x is {x}");
}
}
我正在为 DoSomeWork 方法编写单元测试。这是我现在的,测试通过:
[TestMethod]
public void DoSomeWork_ShouldLogTrace()
{
var mockLogger = new Mock<IMyLogger>(MockBehavior.Strict);
mockLogger.Setup(l => l.Trace("Value of x is 5", It.IsAny<object[]>());
var testWorker = new MyWorker();
testWorker.DoSomeWork(5);
}
我的问题是:有没有一种方法可以将内插字符串而不是字符串内插结果传递给 Trace 设置?
is there a way I can pass the interpolated string to the Trace setup instead of the result of the string interpolation?
没有。 C# 内插字符串不是模板引擎,而是 compile-time 功能。
这一行
Logger.Trace($"Value of x is {x}");
将使用格式化消息调用 Trace
方法,不带参数。
因此,要么使用经典占位符,要么使用记录器引擎,它处理模板参数,例如 Serilog。
您不能传递内插字符串,但您可以验证 Trace
方法是否使用您期望的值调用。
[TestMethod]
public void DoSomeWork_ShouldLogWarning()
{
var mockLogger = new Mock<IMyLogger>(MockBehavior.Strict);
mockLogger.Setup(l => l.Trace(It.IsAny<string>(), It.IsAny<object[]>()));
var testWorker = new MyWorker(mockLogger.Object);
const int expectedValue = 5;
testWorker.DoSomeWork(expectedValue);
mockLogger.Verify(x => x.Trace($"Value of x is {expectedValue}"));
}
在命名空间 System
中使用 FormattableString
type。
也就是说,如果你可以重载:
void Trace(FormattableString formattableMessage);
并确保使用它,然后像这样调用:
Logger.Trace($"Value of x is {x}");
将生成一个 "rich" 对象,您可以在其中检查 .Format
和 .GetArguments
等
解释是 compile-time 表达式 $" ... "
是 隐式转换 到特殊类型 FormattableString
而不是 "interpolate" 马上,而是跟踪格式和每个参数。在实施 // Writes the trace to a log file somewhere
中,对这个富对象使用 .ToString()
以执行实际的插值。
那么最小起订量设置将类似于:
// using nice Strict mock
mockLogger.Setup(l => l.Trace(It.Is(
(FormattableString x) => x.Format == "Value of x is {0}"
)));
已编辑:必须在模拟设置中使用 {0}
和索引 0
,而不是 {x}
。