Moq验证使用在Returns中修改的对象,而不是实际传入的对象

本文关键字:对象 验证 Returns Moq 修改 | 更新日期: 2023-09-27 17:52:33

背景

我有一个类,它使用NHibernate将对象持久化到数据库中。当您为未设置ID的对象调用MergeEntity时,NHibernate会在返回时用ID填充该对象。为了确保我总是使用与NHibernate使用的对象相同的对象,我将更新后的对象从"Save"函数传递回来。

问题

我试图用Moq来模仿同样的行为,Moq通常非常直观且易于使用;但是,我在验证对Save()的调用是否使用了正确的参数时遇到了一些问题。我想验证传入的对象的ID是否为零,然后Save函数是否正确设置了它。不幸的是,当我在Moq.Returns()函数中修改ID时,Moq.Verify函数使用的是修改后的值,而不是传入的ID的值。

为了说明,这里有一个非常基本的类(我覆盖ToString((函数,这样我的测试输出就会显示调用模拟Save((时使用了什么ID(:

public class Class1
{
    private readonly IPersistence _persistence;
    /// <summary>Initializes a new instance of the <see cref="T:System.Object" /> class.</summary>
    public Class1(IPersistence persistence)
    {
        _persistence = persistence;
    }
    public int Id { get; set; }
    public void Save()
    {
       _persistence.Save(this);
    }
    public override string ToString()
    {
        return Id.ToString();
    }
}

这是界面(非常直接(:

public interface IPersistence
{
    Class1 Save(Class1 one);
}

这是我认为应该通过的测试:

[TestFixture]
public class Class1Tests
{
    [Test]
    public void Save_NewObjects_IdsUpdated()
    {
        var mock = new Mock<IPersistence>();
        mock.Setup(x => x.Save(It.IsAny<Class1>()))
            .Returns((Class1 c) =>
            {
                // If it is a new object, then update the ID
                if (c.Id == 0) c.Id = 1;
                return c;
            });
        // Verify that the IDs are updated for new objects when saved
        var one = new Class1(mock.Object);
        Assert.AreEqual(0, one.Id);
        one.Save();
        mock.Verify(x => x.Save(It.Is<Class1>(o => o.Id == 0)));
    }
}

不幸的是,它没有说它从未被要求提供符合该标准的论据。在mock上对Save进行的唯一调用是使用ID为1的对象。我已经验证了当对象进入Returns函数时,它们的ID为0。如果我更新Returns()函数中的值,我是否没有办法区分传递到mock中的内容和更新后的对象?

Moq验证使用在Returns中修改的对象,而不是实际传入的对象

您可以将其切换以验证Save是否使用了正确的对象。然后断言Id已按预期更改。

[Test]
public void Save_NewObjects_IdsUpdated() {
    //Arrange
    var expectedOriginalId = 0;
    var expectedUpdatedId = 1;
    var mock = new Mock<IPersistence>();
    mock.Setup(x => x.Save(It.Is<Class1>(o => o.Id == expectedOriginalId)))
        .Returns((Class1 c) => {
            // If it is a new object, then update the ID
            if (c.Id == 0) c.Id = expectedUpdatedId;
            return c;
        }).Verifiable();
    var sut = new Class1(mock.Object);
    var actualOriginalId = sut.Id;
    //Act
    sut.Save();
    //Assert
    //verify id was 0 before calling method under test
    Assert.AreEqual(expectedOriginalId, actualOriginalId);
    //verify Save called with correct argument
    //ie: an object that matched the predicate in setup
    mock.Verify();
    // Verify that the IDs are updated for new objects when saved
    Assert.AreEqual(expectedUpdatedId, sut.Id);
}

通过在安装时应用过滤器并使其可验证,然后确认该方法实际上是用ID为零的对象调用的。

我已经测试过了,它通过了。为了确认它按预期工作,您可以在执行操作之前将sut的id从预期的开始id更改为。Verify将失败,因为它与谓词不匹配。

这应该能满足你想要实现的目标。