Assert the order of expected calls over instances
How you assert through unit test that an user is authenticated before doing withdraw operation? You can surely verify a method is invoked as expected but if you want to ensure the order right then you might require a little more. JustMock lets you specify the order in which your setups should be executed. This helps you identify the exact way in which a particular logic is implemented.
To begin, lets consider the following context:
User wants to withdraw money from his account. Withdraw operation should validate the following goals:
- It should check if the user is authenticated
- It should get the balance for the authenticated user and check if the amount to be withdrawn is less than or equals to what is specified.
- Do the withdraw operation and return the remaining balance.
Now we have one IUserService interface
- public interface IUserSerivce
- {
- bool IsAuthenticated { get; }
- IUser GetUser();
- }
One IAccountService interface to process the accounts operation:
- public interface IAccountService
- {
- double Withdraw(double amount);
- double GetBalance(IUser user);
- }
The basic AccountRepository class with minimal implementation covering the above context looks like:
- public class AccountRepsotory
- {
- public AccountRepsotory(IUserSerivce userService, IAccountService accountService)
- {
- this.userService = userService;
- this.accountService = accountService;
- }
- public virtual double Withdraw(double amount)
- {
- if (userService.IsAuthenticated)
- {
- if (accountService.GetBalance(userService.GetUser()) >= amount)
- {
- return accountService.Withdraw(amount);
- }
- }
- throw new ArgumentException("TODO");
- }
- private readonly IUserSerivce userService;
- private readonly IAccountService accountService;
- }
Ensuring every step to be executed in an orderly manner , we just need to specify an extra InOrder option in Mock.Arrange that will otherwise fail the test during assert for any change of the expected execution order.
- [TestMethod]
- public void ShouldCheckUserAndBalanceInOrderWhenSpecificAmountIsWithdrawn()
- {
- var userService = Mock.Create<IUserSerivce>();
- var accountService = Mock.Create<IAccountService>();
- var user = Mock.Create<IUser>();
- Mock.Arrange(() => userService.IsAuthenticated).Returns(true).InOrder();
- Mock.Arrange(() => userService.GetUser()).Returns(user).InOrder();
- Mock.Arrange(() => accountService.GetBalance(user)).Returns(1000).InOrder();
- Mock.Arrange(() => accountService.Withdraw(Arg.AnyDouble)).Returns((amount) => 1000 - amount).InOrder();
- var repository = new AccountRepsotory(userService, accountService);
- Assert.AreEqual(990, repository.Withdraw(10));
- Mock.Assert(userService);
- Mock.Assert(accountService);
- }
Let’s remove the line# 13 from AccountRepository.Withdraw that yields:
- public virtual double Withdraw(double amount)
- {
- if (userService.IsAuthenticated)
- {
- return accountService.Withdraw(amount);
- }
- throw new ArgumentException("TODO");
- }
Since we broke the order, the test will fail with the following message:
Here one thing to notice that InOrder is applied to different mock instances within the test method scope that makes it effective in most practical and wide variety of scenarios. I have used Q3 SP build for the purpose (Also available via NuGet).
Hope that helps