如何获取有关将哪个对象传递给模拟方法的详细日志?
How do I get detailed logs about what object was passed to a mocked method?
我 运行 在一段复杂的代码中遇到了一个失败的单元测试,而 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])}"))})"))}");
}
将给出所需的输出:
我 运行 在一段复杂的代码中遇到了一个失败的单元测试,而 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])}"))})"))}");
}
将给出所需的输出: