Verify()和Setup()... VerifyAll()之间的最小起订量

时间:2019-02-21 01:49:52

标签: c# unit-testing nunit moq assert

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

因此,给出了一个非常简单的系统:

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;
}

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

[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();
}

给出系统的当前代码,两个测试均按预期通过。

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

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

    employee.IsEmployed = false;

    return employee;
}

现在,当我运行测试时:

TestViaVerify 通过

TestViaSetupVerifyAll 失败

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

有没有一种方法可以仅使用Verify方法来完成我想要的?不必进行安装... VerifyAll吗?如果没有,我将只使用TestViaVerifyAll()方法。

2 个答案:

答案 0 :(得分:1)

说实话,距离我上次使用moq源代码更新的时间已经超过2年了。 VerifyVerifyAll均基于捕获到的所有伪实例调用(包括参数)。

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

由于捕获的参数是ByRef参数,并且如果最后一段仍然有用,则可以通过在调用robert.IsEmployed = true; / Verify之前添加VerifyAll来导致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之间的组合,因为我不喜欢使用{ {1}}模式:

VerifyAll

答案 1 :(得分:1)

这是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();
}

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