繁体   English   中英

Moq - It.Is 无需指定所有属性

[英]Moq - It.Is without having to specify all properties

我正在使用 Moq 来测试一个方法,被测方法将一个 json 字符串作为参数,将其反序列化为预期的类对象,并使用这个反序列化的类对象调用服务。

我想设置我的服务以期望有问题的类对象的实例具有正确的值,以便我可以验证将 json 字符串反序列化为类对象的步骤是否按预期工作。

目前,我正在使用It.Is<MyClass>(...)方法,但这意味着我必须指定每个属性,并且一些预期的对象包含很多属性。

public class MyClassTests
{
    private Mock<IMyService> _serviceMock = new Mock<IMyService>();

    public void UnitTest01()
    {
        // Arrange
        MyObject myObject = new MyObject
        {
            SomeProperty01 = "One",
            SomeProperty02 = 2,
            Timestamp = DateTime.Now
        };

        string input = Newtonsoft.Json.JsonConvert.SerializeObject(myObject);

        // *** how can I improve this so I dont have to specify every property? ***

        _serviceMock.Setup(x => x.DoSomething(It.Is<MyObject>(o =>
            o.SomeProperty01 == myObject.SomeProperty01
            && o.SomeProperty02 == myObject.SomeProperty02
            && o.Timestamp == myObject.Timestamp
            )));

        MyClass myClass = new MyClass(_serviceMock.Object);

        // Act
        myClass.DoWork(input);

        // Assert
        _serviceMock.Verify();
    }
}

// The class under test
public class MyClass
{
    private readonly IMyService _myService;

    public MyClass(IMyService myService)
    {
        _myService = myService;
    }

    // The method under test. I want to verify that deserialising works as expected, 
    // and that the service is called with the expected argument.
    public void DoWork(string jsonString)
    {
        MyObject deserialised = Newtonsoft.Json.JsonConvert.DeserializeObject<MyObject>(jsonString);

        _myService.DoSomething(deserialised);
    }
}

public class MyService : IMyService
{
    public void DoSomething(MyObject myObject)
    {
        // For the sake of this test, it doesnt matter what this service method does
    }
}

public interface IMyService
{
    void DoSomething(MyObject myObject);
}

public class MyObject
{
    public string SomeProperty01 { get; set; }
    public int SomeProperty02 { get; set; }
    public DateTime Timestamp { get; set; }

    // Loads more properties...
}

是否可以执行类似于It.Is<MyObject>() ,我不必指定每个属性? 一种解决方案可能是It.Is<MyObject>(o => o.Equal(myObject)) MyObject.Equals 方法,即It.Is<MyObject>(o => o.Equal(myObject)) 但是,这并不理想,因为它涉及为了单元测试而更改代码。

考虑改变方法。 从模拟中捕获Callback传递的参数,然后使用FluentAssertions断言它符合预期。

public class MyClassTests {
    private Mock<IMyService> _serviceMock = new Mock<IMyService>();

    public void UnitTest01() {
        // Arrange
        MyObject expected = new MyObject {
            SomeProperty01 = "One",
            SomeProperty02 = 2,
            Timestamp = DateTime.Now
        };

        MyObject actual = null;

        //Setup the mock to capture the passed argument in a callback
        _serviceMock
            .Setup(x => x.DoSomething(It.IsAny<MyObject>())
            .Callback(MyObject arg => { actual = arg; });

        MyClass myClass = new MyClass(_serviceMock.Object);
        string input = Newtonsoft.Json.JsonConvert.SerializeObject(expected);

        // Act
        myClass.DoWork(input);

        // Assert using FluentAssertions
        actual.Should().BeEquivalentTo(expected);
    }
}

现在无需更改测试对象即可进行测试。

您可以使用 KellermanSoftware.CompareNetObjects 库并为这样的对象创建扩展方法:

    public static bool IsEqual<T>(this T value, T other, ComparisonConfig config = null)
    {
        var comparer = new CompareLogic();
        if (config != null) comparer.Config = config;

        var comparisonResult = comparer.Compare(value, other);

        return comparisonResult.AreEqual;
    }

然后你可以使用:

_serviceMock.Setup(x => x.DoSomething(It.Is<MyObject>(o => o.IsEqual(myObject)));

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM