Moq - test call with a parameter that changes during test execution

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 can write unit test to check if the repository is called inside the Execute method, storing the value of the object using the InsertEntity method. In other words, I would like to make sure that when I call the InsertEntity method, the value of the Status property of the Initialized object. So my unit test will 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 does not work even when calling the InsertEntity method with Status = "Initialized" (I debugged this). I think this is because the entity object changes during the execution of the Execute method (at the end, the Status property changes to "Processed"), and Moq checks 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 do my first unit test works is to use the following workaround. I save the value of the Status property using the Moq callback function and state 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 it. I would like to know if there is a way to make Moq check the calls made in the mock object during the execution of the STU (test system), and not after the entire execution has been completed.

thanks

+4
source share
2 answers

IMHO, the last approach (in which you call "hack") is the right approach to check the status value. It became clear to me that what you want to check in this test is that the status is set to "Initialized" at the time of calling the InsertEntity method.

The approach you want to use with Verify is more unclear as to what you are testing. You want to confirm that the InsertEntity call InsertEntity called, or you want to verify that the Initialized parameter, or perhaps both, I don’t know. And if you want to test both, it will actually be two different unit tests.

You can also do the following ...

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

But I don’t like this approach either because it means that instead of <t20> InsertEntity code will be executed, and not your layout, if the status is not the value “Initialized”. Most likely, the unit test will still fail, but the assertion error message returned from the unit test is more obscure (the failure is due to something that happens inside the real InsertEntity method). The callback pinpoints what you are testing in unit test.

+3
source
 [TestMethod] public void ShouldSaveEntityWithStatusInitialized() { // arrange var mock = new Mock<IRepository>(); mock.Setup(m => m.InsertEntity(It.Is<Entity>(e => e.Status == "Initialized"))); var processor = new Processor(mock.Object); // act processor.Execute("test"); // assert mock.Verify(); } 
+1
source

All Articles