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

  1. public interface IUserSerivce
  2. {
  3.     bool IsAuthenticated { get; }
  4.     IUser GetUser();
  5.  
  6. }

One IAccountService interface to process the accounts operation:

  1. public interface IAccountService
  2. {
  3.     double Withdraw(double amount);
  4.     double GetBalance(IUser user);
  5. }

The basic AccountRepository class with minimal implementation covering the above context looks like:

  1. public class AccountRepsotory
  2. {
  3.     public AccountRepsotory(IUserSerivce userService, IAccountService accountService)
  4.     {
  5.         this.userService = userService;
  6.         this.accountService = accountService;
  7.     }
  8.  
  9.     public virtual double Withdraw(double amount)
  10.     {
  11.         if (userService.IsAuthenticated)
  12.         {
  13.             if (accountService.GetBalance(userService.GetUser()) >= amount)
  14.             {
  15.                 return accountService.Withdraw(amount);
  16.             }
  17.         }
  18.  
  19.         throw new ArgumentException("TODO");
  20.     }
  21.  
  22.     private readonly IUserSerivce userService;
  23.     private readonly IAccountService accountService;
  24. }

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.

  1. [TestMethod]
  2. public void ShouldCheckUserAndBalanceInOrderWhenSpecificAmountIsWithdrawn()
  3. {
  4.     var userService = Mock.Create<IUserSerivce>();
  5.     var accountService = Mock.Create<IAccountService>();
  6.     
  7.     var user = Mock.Create<IUser>();
  8.  
  9.     Mock.Arrange(() => userService.IsAuthenticated).Returns(true).InOrder();
  10.     Mock.Arrange(() => userService.GetUser()).Returns(user).InOrder();
  11.     Mock.Arrange(() => accountService.GetBalance(user)).Returns(1000).InOrder();
  12.  
  13.     Mock.Arrange(() => accountService.Withdraw(Arg.AnyDouble)).Returns((amount) => 1000 - amount).InOrder();
  14.  
  15.  
  16.     var repository = new AccountRepsotory(userService, accountService);
  17.  
  18.     Assert.AreEqual(990, repository.Withdraw(10));
  19.  
  20.     Mock.Assert(userService);
  21.     Mock.Assert(accountService);
  22. }

Let’s remove the line# 13 from AccountRepository.Withdraw  that yields:

  1. public virtual double Withdraw(double amount)
  2. {
  3.     if (userService.IsAuthenticated)
  4.     {
  5.         return accountService.Withdraw(amount);
  6.     }
  7.  
  8.     throw new ArgumentException("TODO");
  9. }

 

Since we broke the order, the test will fail with the following message:

image

 

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

No Comments