Reputation: 81
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
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
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