Asserting with JustMock
- by mehfuzh
In this post, i will be digging in a bit deep on Mock.Assert. This is the continuation from previous post and covers up the ways you can use assert for your mock expectations. I have used another traditional sample of Talisker that has a warehouse [Collaborator] and an order class [SUT] that will call upon the warehouse to see the stock and fill it up with items. Our sample, interface of warehouse and order looks similar to : public interface IWarehouse { bool HasInventory(string productName, int quantity); void Remove(string productName, int quantity); } public class Order { public string ProductName { get; private set; } public int Quantity { get; private set; } public bool IsFilled { get; private set; } public Order(string productName, int quantity) { this.ProductName = productName; this.Quantity = quantity; } public void Fill(IWarehouse warehouse) { if (warehouse.HasInventory(ProductName, Quantity)) { warehouse.Remove(ProductName, Quantity); IsFilled = true; } } } Our first example deals with mock object assertion [my take] / assert all scenario. This will only act on the setups that has this “MustBeCalled” flag associated. To be more specific , let first consider the following test code: var order = new Order(TALISKER, 0); var wareHouse = Mock.Create<IWarehouse>(); Mock.Arrange(() => wareHouse.HasInventory(Arg.Any<string>(), 0)).Returns(true).MustBeCalled(); Mock.Arrange(() => wareHouse.Remove(Arg.Any<string>(), 0)).Throws(new InvalidOperationException()).MustBeCalled(); Mock.Arrange(() => wareHouse.Remove(Arg.Any<string>(), 100)).Throws(new InvalidOperationException()); //exercise Assert.Throws<InvalidOperationException>(() => order.Fill(wareHouse)); // it will assert first and second setup. Mock.Assert(wareHouse); Here, we have created the order object, created the mock of IWarehouse , then I setup our HasInventory and Remove calls of IWarehouse with my expected, which is called by the order.Fill internally. Now both of these setups are marked as “MustBeCalled”. There is one additional IWarehouse.Remove that is invalid and is not marked. On line 9 , as we do order.Fill , the first and second setups will be invoked internally where the third one is left un-invoked. Here, Mock.Assert will pass successfully as both of the required ones are called as expected. But, if we marked the third one as must then it would fail with an proper exception. Here, we can also see that I have used the same call for two different setups, this feature is called sequential mocking and will be covered later on. Moving forward, let’s say, we don’t want this must call, when we want to do it specifically with lamda. For that let’s consider the following code: //setup - data var order = new Order(TALISKER, 50); var wareHouse = Mock.Create<IWarehouse>(); Mock.Arrange(() => wareHouse.HasInventory(TALISKER, 50)).Returns(true); //exercise order.Fill(wareHouse); //verify state Assert.True(order.IsFilled); //verify interaction Mock.Assert(()=> wareHouse.HasInventory(TALISKER, 50)); Here, the snippet shows a case for successful order, i haven’t used “MustBeCalled” rather i used lamda specifically to assert the call that I have made, which is more justified for the cases where we exactly know the user code will behave. But, here goes a question that how we are going assert a mock call if we don’t know what item a user code may request for. In that case, we can combine the matchers with our assert calls like we do it for arrange: //setup - data var order = new Order(TALISKER, 50); var wareHouse = Mock.Create<IWarehouse>(); Mock.Arrange(() => wareHouse.HasInventory(TALISKER, Arg.Matches<int>( x => x <= 50))).Returns(true); //exercise order.Fill(wareHouse); //verify state Assert.True(order.IsFilled); //verify interaction Mock.Assert(() => wareHouse.HasInventory(Arg.Any<string>(), Arg.Matches<int>(x => x <= 50))); Here, i have asserted a mock call for which i don’t know the item name, but i know that number of items that user will request is less than 50. This kind of expression based assertion is now possible with JustMock. We can extent this sample for properties as well, which will be covered shortly [in other posts]. In addition to just simple assertion, we can also use filters to limit to times a call has occurred or if ever occurred. Like for the first test code, we have one setup that is never invoked. For such, it is always valid to use the following assert call: Mock.Assert(() => wareHouse.Remove(Arg.Any<string>(), 100), Occurs.Never()); Or ,for warehouse.HasInventory we can do the following: Mock.Assert(() => wareHouse.HasInventory(Arg.Any<string>(), 0), Occurs.Once()); Or, to be more specific, it’s even better with: Mock.Assert(() => wareHouse.HasInventory(Arg.Any<string>(), 0), Occurs.Exactly(1)); There are other filters that you can apply here using AtMost, AtLeast and AtLeastOnce but I left those to the readers. You can try the above sample that is provided in the examples shipped with JustMock.Please, do check it out and feel free to ping me for any issues. Enjoy!!