Fder Aragon
Fder Aragon

Reputation: 81

Moq difference between Verify() and Setup()...VerifyAll()

I was creating a couple of Unit Tests where I wanted to verify if a method is called with a parameter whose properties I was expecting.

So given this very simple system:

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

I want to verify that the DataStore.UpdateEmployee() method is called when the Employee.IsEmployed property is set to false. So here are two test cases that I believe should accomplish the same thing.

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

Given the current code of the system, both test passes as expected.

Now say another developer comes along and accidentally moved the setting of the Employee.IsEmployed = false; after the DataStore.UpdateEmployee() method. Now in that case, I want my tests to fail because the employee will not be marked as unemployed in the DB.

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

    employee.IsEmployed = false;

    return employee;
}

Now when I run the test:

TestViaVerify Passes

TestViaSetupVerifyAll Fails

I was expecting both of them to fail but it looks like for the TestViaVerify() method, the lambda in the method is executed at the end of the test wherein the Employee.IsEmployed is already set to false.

Is there a way to accomplish what I want with just using the Verify method? And not have to do Setup...VerifyAll? If there is none I'll just go with TestViaVerifyAll() approach.

Upvotes: 6

Views: 4534

Answers (2)

Johnny
Johnny

Reputation: 9509

This is expected behavior in moq as arguments captured by invocation are compared by identity, using Equals not by value. Once you change the captured arguments you actually directly change the invocation. Then later on when you verify, these objects are not the same anymore. As @Old Fox already provided one solution I will just add one more. You could use Verify() instead of VerifyAll() the difference is that the first one will only check setups marked as Verifiable(). In your case something like this:

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

If you mark setup as Verifiable() you could be able to capture the particular invocation with the state of the object you actually expect.

Upvotes: 1

Old Fox
Old Fox

Reputation: 8725

To be honest it is been more then 2 years since the last time I was updated with moq source code. Both Verify and VerifyAll are based on that every invocation of the fake instance is being captured(include the parameters).

Verify will looking for method/property invocation and verifies the captured invocations(with their captured parameters) while, VerifyAll will take all the setup methods and do the same as Verify method.

Since the captured parameter is ByRef parameter and if the last paragraph is still relevant you can cause your UTs failed simply by add robert.IsEmployed = true; before invoke Verify/VerifyAll:

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

I think that in the past I answered something similar(with more examples), the way I was workaround this problem is a combination between Setup and Callback since I don't like to use VerifyAll pattern:

....
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);

Upvotes: 1

Related Questions