Mocks can be your friend, or your worst nightmare

Take a look at the following test:

   1: public void EmployeeServicesTest_GetAllEmployeesByCompanyId_When_Employees_Are_Marked_Returns_Non_Marked_Employees()
   2:        {
   3:            var guid = Guid.NewGuid();
   4:            var guid2 = Guid.NewGuid();
   5:            var guidComp= Guid.NewGuid();
   6:  
   7:            MockRepository mocks = new MockRepository();
   8:  
   9:            IEmployeeRepository mockRepository = (IEmployeeRepository)mocks.StrictMock(typeof(IEmployeeRepository));
  10:  
  11:  
  12:            User user = new User();
  13:            user.Id = Guid.NewGuid();
  14:  
  15:            Employee deletedEmployee = new Employee();
  16:            deletedEmployee.Id = guid;
  17:            deletedEmployee.DeletedBy = user;
  18:  
  19:            Employee validEmployee = new Employee();
  20:            validEmployee.Id = guid;
  21:  
  22:            Expect.Call(mockRepository.GetEmployeesByCompanyId<Employee>(guidComp)).Return(new List<Employee>() { deletedEmployee, validEmployee });
  23:  
  24:            mocks.ReplayAll();
  25:  
  26:            var EmployeeServices = new EmployeeServices(mockRepository);
  27:  
  28:            var testList = EmployeeServices.GetAllEmployeesByCompanyId(guidComp);
  29:  
  30:            Assert.AreEqual(1, testList.Count);
  31:  
  32:            mocks.VerifyAll();
  33:        }

 

I won’t get into the naming conventions since I’ve covered those in previous blog entries. This test has another problem. It’s trying to validate that when you ask for a list of employees, only those that are not marked as deleted are returned. To simulate the data returned, it is making use of a mock repository (RhinoMocks).

Before looking at the issue, let’s decide what the purpose of the test is. We want to test that when a system has two employees, one of whom is marked for deletion, a call to GetAllEmployeeesByCompanyId returns only one employee. Therefore the SUT is that method call.

In line 9, a mock of the repository is created, of type StrictMock. This is a particular call of RhinoMocks, but pretty much all mocking frameworks have the same feature. A strict mock indicates that every expected call should be made. Therefore, when on line 32 a call to VerifyAll is made, this test will fail if mockRepository.GetEmployeesByCompanyId has not been called with those exact arguments. In our case, the test passes and all is fine. Or is it?

The problem however if we change the internal functionality of EmployeeServices.GetAllEmployeesByCompanyId. If some reason or other we make a second call to EmployeeRepository, that doesn’t change the outcome of the method, this test will fail.

Why is that? The reason isn’t so much that we are using StrictMock as opposed to DynamicMock. The problem is that we are trying to test too many things in the same test. In actual fact, in this particular case, only ONE thing should be tested: retrieving one employee out of two. However the introduction of a StrictMock and the VerifyAll call has had the side-effect of introducing an interaction test, something we are not interested in.

Mocks can be great and very powerful. But use them wisely. If you start creating strict mocks (which should only be used for interaction/behavioural testing), then you’re just making your tests very brittle. The slightest change in behaviour, even though it’s encapsulated inside the method being tested can break your test. This pretty much defeats the whole advantage of unit tests. We create tests to isolate functionality and test that functionality in isolation. As soon as we tightly couple our tests to a particular implementation, and when the system under test is not trying to validate that implementation, the whole thing blows up. In another blog post, I’ll re-factor the test to make it much simpler and less volatile.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s