c# - 如何获取有关传递给模拟方法的对象的详细日志?
问题描述
我在一段复杂的代码中遇到了失败的单元测试,而 Moq 日志记录并没有为我做这件事。我需要知道我期望调用的方法与实际调用的方法之间有什么不同。我最终在单元测试中附加了一个调试器,这样我就可以查看传递给 moq 的对象并手动比较它的所有值。Verify
如果我从错误消息中获得更多信息,那么所有这些努力都是不必要的。
如何记录这两件事之间的差异?
- 我期望用什么来调用模拟方法
- 模拟方法实际上是用什么调用的
一个简化的例子:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
namespace MyNamespace
{
[TestClass]
public class MyTestClass
{
[TestMethod]
public void MyTestMethod()
{
// Arrange
var serviceMock = new Mock<IService>();
var model = new Model
{
Word = "Foo",
Number = 1
};
var servicecaller = new ServiceCaller(serviceMock.Object);
// Act
servicecaller.CallService(model);
// Assert
serviceMock.Verify(mock =>
mock.Call(
It.Is<Model>(m =>
m.Word == "Bar"
&& m.Number == 1)));
}
}
public class ServiceCaller
{
private IService _service;
public ServiceCaller(IService service)
{
_service = service;
}
public void CallService(Model model)
{
_service.Call(model);
}
}
public interface IService
{
void Call(Model model);
}
public class Model
{
public int Number { get; set; }
public string Word { get; set; }
}
}
当您运行此测试时,它会失败并显示以下消息:
Test method MyNamespace.MyTestClass.MyTestMethod threw exception: Moq.MockException: Expected invocation on the mock at least once, but was never performed: mock => mock.Call(It.Is<Model>(m => m.Word == "Bar" && m.Number == 1)) No setups configured. Performed invocations: IService.Call(Model)
但我希望它记录以下内容:
Performed invocations: IService.Call(Model { Word: "Bar", Number: 1 })
甚至更好:
"Assert failed for the object passed to IService.Call: The Model.Word 'Foo' is not equal to 'Bar'".
解决方案
您可能必须通过Mock.Invocations
它验证调用,它是模拟上所有调用的序列以及提供的参数。类似于以下内容:
var callInvocations = serviceMock.Invocations.Where(x => x.Method.Name.Equals(nameof(IService.Call)));
var matchingInvocations = callInvocations.Where(x =>
{
var model = x.Arguments.First() as Model;
return model.Word.Equals("Bar") && model.Number == 1;
});
if (!matchingInvocations.Any())
{
throw new Exception($"Performed invocations:{Environment.NewLine}{string.Join(Environment.NewLine, callInvocations.Select(x => $"{x.Method.DeclaringType.Name}.{x.Method.Name}({string.Join(", ", x.Method.GetParameters().Select((y, i) => $"{y.ParameterType.Name} {JsonConvert.SerializeObject(x.Arguments[i])}"))})"))}");
}
将给出所需的输出: