【问题标题】:Moq difference between Verify() and Setup()...VerifyAll()验证()和设置()之间的最小起订量差异...验证所有()
【发布时间】:2019-02-21 01:49:51
【问题描述】:

我正在创建几个单元测试,我想验证是否使用我期望的参数调用方法。

所以给定这个非常简单的系统:

public class Employee
{
    public bool IsEmployed { get; set; }
}

public class DataStore
{
    public void UpdateEmployee(Employee obj)
    {
        // Save in DB
    }
}

public interface IDataStore
{
    void UpdateEmployee(Employee employee);
}

public Employee FireEmployee(IDataStore dataStore, Employee employee)
{
    employee.IsEmployed = false;

    dataStore.UpdateEmployee(employee);

    return employee;
}

我想验证 Employee.IsEmployed 属性设置为 false 时是否调用了 DataStore.UpdateEmployee() 方法。所以这里有两个我认为应该完成同样事情的测试用例。

[Test]
public void TestViaVerify()
{
    //Arrange
    Mock<IDataStore> dataStore = new Mock<IDataStore>();
    var robert = new Employee { IsEmployed = true };

    //Act
    FireEmployee(dataStore.Object, robert);

    //Assert
    dataStore.Verify(x => x.UpdateEmployee(It.Is<Employee>(e => e.IsEmployed == false)), Times.Once);
}

[Test]
public void TestViaSetupVerifyAll()
{
    //Arrange
    Mock<IDataStore> dataStore = new Mock<IDataStore>();
    dataStore.Setup(x => x.UpdateEmployee(It.Is<Employee>(e => e.IsEmployed == false)));

    var robert = new Employee { IsEmployed = true };

    //Act
    FireEmployee(dataStore.Object, robert);

    //Assert
    dataStore.VerifyAll();
}

鉴于系统的当前代码,两个测试都按预期通过。

现在说另一个开发人员意外地在DataStore.UpdateEmployee() 方法之后移动了Employee.IsEmployed = false; 的设置。现在在这种情况下,我希望我的测试失败,因为该员工不会在数据库中被标记为失业。

public Employee FireEmployee(IDataStore dataStore, Employee employee)
{
    dataStore.UpdateEmployee(employee);

    employee.IsEmployed = false;

    return employee;
}

现在当我运行测试时:

TestViaVerify 通过

TestViaSetupVerifyAll 失败

我原以为它们都会失败,但对于 TestViaVerify() 方法来说,该方法中的 lambda 在测试结束时执行,其中 Employee.IsEmployed 已经设置为 false。

有没有办法只使用验证方法来完成我想要的?并且不必进行设置...VerifyAll?如果没有,我将使用TestViaVerifyAll() 方法。

【问题讨论】:

  • 这似乎是moq 的问题。当您通过引用更改已捕获的对象时。您能否在调用dataStore.UpdateEmployee 之前尝试制作副本。我的假设是Verify 也会失败。

标签: c# unit-testing nunit moq assert


【解决方案1】:

说实话,距离我上次更新 moq 源代码已经过去了 2 年多。 VerifyVerifyAll 都是基于每次调用假实例都被捕获(包括参数)。

Verify 将寻找方法/属性调用并验证捕获的调用(及其捕获的参数),而VerifyAll 将采用所有设置方法并与Verify 方法执行相同的操作。

由于捕获的参数是ByRef 参数,如果最后一段仍然相关,您只需在调用Verify/VerifyAll 之前添加robert.IsEmployed = true; 即可导致UT 失败:

[Test]
public void TestViaVerify()
{
    ....
    robert.IsEmployed = true; // will make this UT to failed
    //Assert
    dataStore.Verify(x => x.UpdateEmployee(It.Is<Employee>(e => e.IsEmployed == false)), Times.Once);
}

[Test]
public void TestViaSetupVerifyAll()
{
    ....
    robert.IsEmployed = true; // will make this UT to failed
    //Assert
    dataStore.VerifyAll();
}

我认为过去我回答了类似的问题(有更多示例),我解决此问题的方法是 SetupCallback 之间的组合,因为我不喜欢使用 VerifyAll 模式:

....
var invokedCorrectly = false;
dataStore.Setup(x => x.UpdateEmployee(It.Is<Employee>(e => e.IsEmployed == false)))
         .Callback<Employee>(x=> invokedCorrectly = true);

//Act
FireEmployee(dataStore.Object, robert);

//Assert
Assert.IsTrue(invokedCorrectly);

【讨论】:

    【解决方案2】:

    这是moq 中的预期行为,因为调用捕获的参数是按身份比较的,使用Equals 而不是按值。一旦你改变了捕获的参数,你实际上直接改变了调用。然后稍后当您验证这些对象不再相同时。正如@Old Fox 已经提供了一种解决方案,我将再添加一个。您可以使用Verify() 而不是VerifyAll(),不同之处在于第一个只会检查标记为Verifiable() 的设置。在你的情况下是这样的:

    [Test]
    public void TestViaSetupVerifyAll()
    {
        //Arrange
        Mock<IDataStore> dataStore = new Mock<IDataStore>();
        dataStore
            .Setup(x => x.UpdateEmployee(It.Is<Employee>(e => e.IsEmployed == false)))
            .Verifiable();
    
        var robert = new Employee { IsEmployed = true };
    
        //Act
        FireEmployee(dataStore.Object, robert);
    
        //Assert
        dataStore.Verify();
    }
    

    如果您将 setup 标记为 Verifiable(),您可以捕获具有您实际期望的对象状态的特定调用。

    【讨论】:

      猜你喜欢
      • 2012-08-09
      • 2011-06-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-03
      • 2018-11-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多