One Logical Assertion Per Test

Having had the chance to get some good feedback from Aaron on my One Mock Per Test post I’m refining the TDD rules I’ll live by going forward.

I still believe tests should have a single assertion, but as Ayende points out occasionally a single call to Assert.ThisOrThat won’t cut it. What we really want to preserve a single logical assertion. Put another way, if a test has multiple assertions, each assertion must exhibit a high cohesion with the specification described by the test. In Ayende’s example each assertion has a shared mission: verify the contents of a row-like structure. The nature of the specification dictates more than one Assert. Without having to use too much imagination we can see in some circumstances it’d be overkill to check that each individual cell is set with a separate test, given that he’s working at the row level. I’ll pretend his specification says “a row is put in a certain state.”

An example test that uses multiple assertions in a highly cohesive manner:

[Test]
public void A_user_sees_results_of_a_gold_customer_search()
{
   ICustomerSearchService mockService
      = _mocks.DynamicMock<ISearchService>();

   ISearchView stubView
      = _mocks.Stub<ISearchView>();

   // puts 26 customer entries in a fake DTO
   CustomerSearchResultDTO fakeResults = GetFakeResults(26);

   using (_mocks.Record())
   {
       Expect
         .Call(mockService.FindCustomersByLevel(CustomerLevel.Gold))
         .Returns(fakeResults);
   }

   using (_mocks.Playback())
   {
      SearchPresenter presenter = new SearchPresenter(stubView, stubService);
      presenter.FindGoldCustomers();
      Assert.AreEqual(”Gold Customers: 26″, stubView.Title);
   }
}

In the example there are really three explicit, but highly cohesive, assertions in this contrived example:

  1. I expect that ISearchView.FindCustomersByLevel will be called
  2. ISearchView.FindCustomersByLevel will be given an enum argument CustomerLevel.Gold
  3. The view (technically a stub) will have the title “Gold Customers: 26″

In the real world I’d likely treat the stubView as a mock object and set an expectation that it’s Data property receives the fakeResults. Another thing I’d probably do is stick with all interaction testing and treat stubView as a real mock object. I’d set an expectation that Title was set instead of asserting Title was set to a specific value after the fact. To me it’s a clear case of six to one, half-a-dozen to another but I suppose there’s something to be said for not paradigm shifting too much between interaction/state testing if it can be avoided.

However you cut it, these are highly cohesive assertions; they belong together and describe a complete interaction. The test is still satisfyingly small so I continue the benefit of quick return to test mode after passing green.

As an aside: I like Aaron and Jacob’s advice about using DynamicMocks (when using Rhino Mocks) as a way of building a more flexible refactoring/change safety net from your tests.

Post a Comment

*Required
*Required (Never published)