Background
I have a class that uses NHibernate to persist objects to a database. When you call MergeEntity
for an object that does not have an ID set, NHibernate populates that object with an ID when it is returned. In order to make sure I always use the same object as NHibernate is using, I pass that updated object back from my "Save
" function.
Problem
I am trying to mock that same behavior using Moq, which is usually very intuitive and easy to use; however, I am having some trouble verifying that the calls to Save()
are being made with the correct arguments. I would like to verify that the ID of the object being passed in is zero, then that it is set properly by the Save
function. Unfortunately, when I modify the ID in the Moq.Returns()
function, the Moq.Verify
function uses the modified value rather than the value of the ID that was passed in.
To illustrate, here is a very basic class (I override the ToString() function so my test output will show me what ID was used when the mocked Save() is called):
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();
}
}
Here is the Interface (very straight forward):
public interface IPersistence
{
Class1 Save(Class1 one);
}
And here is the test that I think should pass:
[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)));
}
}
Unfortunately, it fails saying that it was never called with arguments that match that criteria. The only invocation to Save
that was made on the mock is with an object that had an ID of 1. I have verified that the objects have an ID of 0 when they enter the Returns function. Is there no way for me to distinguish what was passed into the mock from what those objects are updated to be if I update the values in my Returns()
function?
You can switch it up to verify that the Save was done with the correct object. And then assert that the Id was changed as expected.
[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);
}
By applying the filter on setup you and making it verifiable then you confirm that the method was actually called with an object that had an ID of zero.
I've tested this and it passes. To confirm that is works as expected you can change the id of sut from expected start id before doing act. Verify would fail as it does not match predicate.
This should satisfy what you are trying to achieve.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With