首页 > 解决方案 > 如何获取有关传递给模拟方法的对象的详细日志?

问题描述

我在一段复杂的代码中遇到了失败的单元测试,而 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'".

标签: c#unit-testingmoq

解决方案


您可能必须通过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])}"))})"))}");
}

将给出所需的输出:

在此处输入图像描述


推荐阅读