Monday, June 25, 2012

Testing for execution sequence

I ran into a problem the other day where I was doing some cost calculations and wasn’t getting the expected result, the problem turned out to be I was doing the calculations before everything got loaded, simple mistake.

Thought it would be interesting to show the solution I used for testing, lets say we have a simple method for updating an employee record.
   1: public bool UpdateEmployee(EmployeeDto updateEmployee)
   2: {
   3:     var result = false;
   4:     if(AuthRequests.UserCanUpdateEmployee(User.Id,updateEmployee.Id))
   5:     {
   6:         result = EmployeeRepository.UpdateEmployee(updateEmployee);
   7:     }
   8:     return result;
   9: }
fairly simple, you call AuthRequests to see if the user and update the employee and then you update the employee.  Here is an example test class for it
   1: [TestFixture]
   2: public class When_Updating_Employee_Information_Test: Test_Context<EmployeeRequests>
   3: {
   4:     public Mock<IAuthRequests> AuthRequestsMock;
   5:     public bool ExpectedAuth;
   6:     public int UserId;
   7:     public int EmployeeId;
   8:     public bool Actual;
   9:     public bool Expected;
  10:     public EmployeeDto UpdateEmployee;
  11:     public EmployeeDto SentEmployee;
  12:     public UserDto ExpectedUser;
  13:     public Mock<IEmployeeRepository> MockEmployeeRepository;
  14:  
  15:     public override void Context()
  16:     {
  17:         base.Context();
  18:         UserId = 12;
  19:         EmployeeId = 14;
  20:         Expected = true;
  21:         ExpectedAuth = true;
  22:         UpdateEmployee = new EmployeeDto{Id = EmployeeId};
  23:         Target.User.Id = UserId;
  24:         AuthRequestsMock = new Mock<IAuthRequests>();
  25:         MockEmployeeRepository = new Mock<IEmployeeRepository>();
  26:         
  27:         AuthRequestsMock.Setup(x => x.UserCanUpdateEmployee(UserId, EmployeeId)).Returns(ExpectedAuth);
  28:         MockEmployeeRepository.Setup(x => x.UpdateEmployee(It.IsAny<EmployeeDto>())).Callback<EmployeeDto>(
  29:             x => SentEmployee = x).Returns(Expected);
  30:  
  31:         Target.AuthRequests = AuthRequestsMock.Object;
  32:         Target.EmployeeRepository = MockEmployeeRepository.Object;
  33:     }
  34:  
  35:     public override void Because()
  36:     {
  37:         Actual = Target.UpdateEmployee(UpdateEmployee);
  38:     }
  39:  
  40:     [Test]
  41:     public void Calls_AuthRequests_UserCanUpdateEmployee_Test()
  42:     {
  43:         AuthRequestsMock.Verify(x=>x.UserCanUpdateEmployee(UserId, EmployeeId), Times.Once());
  44:     } 
  45:  
  46:     [Test]
  47:     public void Calls_EmployeeRepository_UpdateEmployee_Test()
  48:     {
  49:         MockEmployeeRepository.Verify(x => x.UpdateEmployee(It.IsAny<EmployeeDto>()), Times.Once());
  50:     }
  51:  
  52:     [Test]
  53:     public  void Sends_expected_Emplyee_test()
  54:     {
  55:         Assert.AreEqual(UpdateEmployee.Id, SentEmployee.Id);
  56:     }
  57:  
  58:     [Test]
  59:     public void Returns_Expected_Result_Test()
  60:     {
  61:         Assert.IsTrue(Actual);
  62:     }
  63: }
the only problem is the tests still pass if the method looks like this
   1: public bool UpdateEmployee(EmployeeDto updateEmployee)
   2: {
   3:     var result = EmployeeRepository.UpdateEmployee(updateEmployee);
   4:     AuthRequests.UserCanUpdateEmployee(User.Id,updateEmployee.Id);
   5:     return result;
   6: }
so lets add some sequence tests, basically we add a counter and a dictionary<string,int> to record what was executed and it what order using the Moq callback.
   1: [TestFixture]
   2: public class When_Updating_Employee_Information_With_sequence_Test : Test_Context<EmployeeRequests>
   3: {
   4:     public Mock<IAuthRequests> AuthRequestsMock;
   5:     public bool ExpectedAuth;
   6:     public int UserId;
   7:     public int EmployeeId;
   8:     public bool Actual;
   9:     public bool Expected;
  10:     public EmployeeDto UpdateEmployee;
  11:     public EmployeeDto SentEmployee;
  12:     public UserDto ExpectedUser;
  13:     public Mock<IEmployeeRepository> MockEmployeeRepository;
  14:     public int Counter;
  15:     public Dictionary<string, int> Sequence; 
  16:  
  17:     public override void Context()
  18:     {
  19:         base.Context();
  20:         UserId = 12;
  21:         EmployeeId = 14;
  22:         Expected = true;
  23:         ExpectedAuth = true;
  24:         UpdateEmployee = new EmployeeDto { Id = EmployeeId };
  25:         Counter = 0;
  26:         Sequence = new Dictionary<string, int>();
  27:         Target.User.Id = UserId;
  28:         AuthRequestsMock = new Mock<IAuthRequests>();
  29:         MockEmployeeRepository = new Mock<IEmployeeRepository>();
  30:  
  31:         AuthRequestsMock.Setup(x => x.UserCanUpdateEmployee(UserId, EmployeeId))
  32:             .Callback(() => Sequence.Add("AuthRequests.UserCanUpdateEmployee", Counter++)).Returns(ExpectedAuth);
  33:         MockEmployeeRepository.Setup(x => x.UpdateEmployee(It.IsAny<EmployeeDto>()))
  34:             .Callback<EmployeeDto>(x =>
  35:                  {
  36:                     SentEmployee = x;
  37:                     Sequence.Add("EmployeeRepository.UpdateEmployee", Counter++);
  38: }).Returns(Expected);
  39:  
  40:         Target.AuthRequests = AuthRequestsMock.Object;
  41:         Target.EmployeeRepository = MockEmployeeRepository.Object;
  42:     }
  43:  
  44:     public override void Because()
  45:     {
  46:         Actual = Target.UpdateEmployee(UpdateEmployee);
  47:     }
  48:  
  49:     [Test]
  50:     public void Calls_AuthRequests_UserCanUpdateEmployee_Test()
  51:     {
  52:         AuthRequestsMock.Verify(x => x.UserCanUpdateEmployee(UserId, EmployeeId), Times.Once());
  53:     }
  54:  
  55:     [Test]
  56:     public void Calls_AuthRequests_UserCanUpdateEmployee_Inorder_Test()
  57:     {
  58:         Assert.AreEqual(0, Sequence["AuthRequests.UserCanUpdateEmployee"]);
  59:     }
  60:  
  61:     [Test]
  62:     public void Calls_EmployeeRepository_UpdateEmployee_Test()
  63:     {
  64:         MockEmployeeRepository.Verify(x => x.UpdateEmployee(It.IsAny<EmployeeDto>()), Times.Once());
  65:     }
  66:  
  67:     [Test]
  68:     public void Calls_EmployeeRepository_UpdateEmployee_Inorder_Test()
  69:     {
  70:         Assert.AreEqual(1, Sequence["EmployeeRepository.UpdateEmployee"]);
  71:     }
  72:  
  73:     [Test]
  74:     public void Sends_expected_Emplyee_test()
  75:     {
  76:         Assert.AreEqual(UpdateEmployee.Id, SentEmployee.Id);
  77:     }
  78:  
  79:     [Test]
  80:     public void Returns_Expected_Result_Test()
  81:     {
  82:         Assert.IsTrue(Actual);
  83:     }
  84: }
for the full source see the sample application here.

No comments: