Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moq - verifying a call with parameter that is changed during the execution of the test

Given the following code

public class Entity
{
    public string Name { get; set; }
    public string Status { get; set; }
}

public interface IRepository
{
    void InsertEntity(Entity entity);
    void UpdateEntity(Entity entity);
}

public class Processor
{
    private IRepository _repository;

    public Processor(IRepository repository)
    {
        _repository = repository;
    }

    public void Execute(string name)
    {
        var entity = new Entity() { Name = name, Status = "Initialized" };
        _repository.InsertEntity(entity);
        // do other things with the entity
        entity.Status = "Processed";
        _repository.UpdateEntity(entity);
    }
}

I could write a unit test to verify if the repository is called inside the Execute method, saving the value of the entity with the method InsertEntity. In other words, I'd like to make sure that when InsertEntity is called, the value of Status property of the entity is "Initialized". So my unit test would be like this:

[TestMethod]
public void ShouldSaveEntityWithStatusInitialized()
{
    var mock = new Mock<IRepository>();
    var processor = new Processor(mock.Object);
    processor.Execute("test");
    mock.Verify(m => m.InsertEntity(It.Is<Entity>(e => e.Status == "Initialized")), Times.Once()); // fail
}

However, this code fails even calling InsertEntity method with Status = "Initialized" (I've debugged that). I think it's because the entity object is changed during the execution of Execute method (at the end the Status property is changed to "Processed") and Moq verifies the call against the changed object. In fact, this other unit test works well.

[TestMethod]
public void ShouldUpdateEntityWithStatusProcessedAtTheEnd()
{
    var mock = new Mock<IRepository>();
    var processor = new Processor(mock.Object);
    processor.Execute("test");
    mock.Verify(m => m.InsertEntity(It.Is<Entity>(e => e.Status == "Processed")), Times.Once());
}

The only way I found to make my first unit test works is using the following workaround. I save the value of Status property using a callback feature of Moq, and assert that later.

[TestMethod]
public void ShouldSaveEntityWithStatusInitialized_withWorkaround()
{
    var mock = new Mock<IRepository>();
    var processor = new Processor(mock.Object);
    string status = string.Empty;
    mock.Setup(m => m.InsertEntity(It.IsAny<Entity>())).Callback((Entity e) => status = e.Status);
    processor.Execute("test");
    Assert.AreEqual("Initialized", status);
}

But I didn't like that. I would like to know if there's a way to make Moq verify the calls made to the mock object during the execution of the STU (system under test), not after all execution is done.

Thanks

like image 648
Fabio Avatar asked Oct 21 '22 21:10

Fabio


1 Answers

IMHO, The last approach (in which you call the "hack") is the correct approach to test the value of the status. It's more clear to me that what you want to verify in this test is that the status is set to "Initialized" at the time the InsertEntity method is called.

The approach you want to use with the Verify is more obscure as to exactly what you are testing. Are you wanting to confirm the InsertEntity is called or are you wanting to test that the parameter is "Initialized", or maybe even both, I don't know. And if you are wanting to test both then this would actually be two different unit tests.

You could also do the following...

mock.Setup(m => m.InsertEntity(It.Is<Entity>(e => e.Status == "Initialized")));

But I do not like this approach either because it means the production InsertEntity code will run instead of your mock when the status is not a value of "Initialized". More then likely the unit test will still fail but the assertion failure message returned from the unit test is more obscure (the failure occurs because of something happening inside the real InsertEntity method). The callback states exactly what you are testing in the unit test.

like image 186
Gene S Avatar answered Oct 24 '22 17:10

Gene S