Unit test formats, Episode II : Inheritance strikes back

This is the second post in a series on unit test patterns.

Referring to the base pattern from the previous post, I broke a test or specification down into three parts:

  • a process
  • a situation
  • and expectations

In this post, I expand the base pattern to specify behavior for different situations (the second bullet point) of the same process. There are many ways to specify behavior for different situations, including using frameworks like NSpec or SpecFlow that have more complex syntax. I don’t mind saying that I’m not there yet, but I’m eying them. So instead, I’ll do this with NUnit and…inheritance…

An aside: inheritance has had an interesting rise and fall in popularity. And I agree with the “market’s correction.” It seems that there was a point where OO was about objects and inheritance and little else. And when all us developers jumped out of the gate with nothing but “IS A” in our heads, well, when all you’ve got is a hammer… Anyway, good or bad, I’m using inheritance and so far it’s helping, not hurting.

Returning to the UserInvitationManager class of the last post, I now forget what that class does, exactly. What about user invitations does it manage? Blast my absent-mindedness! Well, I can look at the public methods and get a much better idea– InviteUser()– ah, ok; but I don’t like that I had to do that. Not at all. I want to rename that guy to something just in-my-face obvious: InviteUserProcess. Of course it’s interfaced, so there’s also an IInviteUserProcess now. You could also call the interface just IInviteUser. And I’ll just replace the redundancy of InviteUserProcess.InviteUser() with InviteUserProcess.Execute() or .Run(), .Invoke(), etc.

Now, InviteUserProcess is a very specific name. I’ve kind of locked myself in here. I’m limited to pretty much that one function and I don’t have the freedom to put a lot of other user invitation-related methods on it like I did with the UserInvitationManager…GREAT!!! In fact, I like that so well that I’ll go ahead and update the names of the dependencies as well, e.g., from IUserInvitationSender to ISendUserInvitationProcess.

Anyway, this renames the specification file and its namespace, but I think I can also eliminate that When_ namespace since the new name defines the “when”. Here’s the last post’s specification code with these changes…

namespace UserManagerSpecs
namespace InviteUserProcessSpecs
{
 namespace When_inviting_a_user_to_join_an_account
 {
      [TestFixture]
      public class Given_an_email_address_and_an_account_number
      {
         [Test]
 public void Then_the_user_invitation_manager_should_pre_register_that_email_address_to_the_account()
         public void This_process_should_preregister_that_email_address_to_the_account()
         {

That defines only one situation: given an email address and an account number. The different situations that are the focus of this post are going to arise out of an increased awareness that just any ol’ string won’t do for either the email address or the account number. We need to validate those two strings. Now, if there’s anything I hate doing, it’s validation, but there’s no sense crying about it; it has to be done. So here are the situations…

  • Given a valid email address and a valid account number
  • Given an invalid email address and a valid account number
  • Given a valid email address and an invalid account number
  • Given an invalid email address and an invalid account number

In the first situation, my expectations/desired behavior is super simple– I want the process to 1) preregister that email address and 2) send the invitation. Oh, in the last version, we assumed the values were fine so we didn’t have anything to return. The real world is a messy place, ain’t it? Anway, this adds one more expectation: 3) let the consumer of this process know that it worked. On the flip side, if either parameters are invalid, the consumer of this process is going to want to know something went wrong and what that was, and I certainly don’t want to preregister the email address or send the invitation in such a case. So, here I list these expected behaviors…

  • Given a valid email address and a valid account number
    – The process should preregister the email address with the account
    – The process should send an invitation to the email address
    – The process should return a success notice
  • Given an invalid email address and a valid account number
    – The process should not preregister anything
    – The process should not send an invitation
    – The process should return an invalid email notice
  • Given a valid email address and an invalid account number
    – The process should not preregister anything
    – The process should not send an invitation
    – The process should return an invalid account notice
  • Given an invalid email address and an invalid account number
    – The process should not preregister anything
    – The process should not send an invitation
    – The process should return an invalid email and account notice

So I haven’t written any code yet, but I’ve got an excellent idea of the problem domain I’m up against and how the system should behave. Writing these specifications for the behavior has drawn all that out.

The InviteUserProcess has gained the new responsibilities of knowing how to recognize these two parameters as valid. I could load up this process with some regex’s or something, but that would be harder to test. Because it will be easier to test, I’ll imagine there’s an IValidateEmailAddressProcess and an IValidateAccountProcess, and then stub them. That’s actually quite a bit easier. My specifications will not have to simultaneously deal with defining expectations for what to do because of an invalid email address and what makes for an invalid email address. Or what makes for an invalid account number. I’m dropping that extra responsibility (think the ‘S’ in SOLID). Note: “Because it will be easier to test, I’ll…” is the effect of the driving nature of TDD/BDD, and here I see how that drive pushes me toward better code. Remember, TDD is not just about having an automated test, it’s also about improving how you code (and other things, too).

Now that I know my situations and expected behaviors, I’ll break out my inheritance pattern for building the spec classes. The inheritance pattern creates a base class which defines all the common parties to greatly reduce redundant setup code for the derived classes. I’ll also bother giving this base class a name that will satisfy the “IS A” criteria, “InviteUserProcessSpec,” though that may be a bit superfluous.

public class InviteUserProcessSpec
{
   // System under test
   protected InviteUserProcess inviteUserProcess;

   // Dependencies
   protected IValidateEmailAddressProcess validateEmailProcess = MockRepository.GenerateMock<IValidateEmailAddressProcess>();
   protected IValidateAccountProcess validateAcctProcess = MockRepository.GenerateMock<IValidateAccountProcess>();
   protected IPreregisterEmailProcess preregisterEmailProcess = MockRepository.GenerateMock<IPreregisterEmailProcess>();
   protected ISendUserInvitationProcess sendInvitationProcess = MockRepository.GenerateMock<ISendUserInvitationProcess>();

   // Parameters
   protected String emailAddress = "doesntMatter";
   protected String accountNumber = "doesntMatterEither";

   // Return value
   protected InviteUserResponse response;

   public virtual void SetupTestFixture()
   {
       inviteUserProcess = new InviteUserProcess( validateEmailProcess, validateAcctProcess, preregisterEmailProcess, sendInvitationProcess );
   }
}

You can see that, as I mentioned in the previous post, I’ve still got four kinds of players: the system under test, the dependencies, the parameters, and the return value. You may also notice that in this class I’ve introduced a return value: InviteUserResponse. This is just an enum defined with the different response cases (e.g. Successful, InvalidEmailAddress, etc.). It could have been as simple as a string with a message or more complex like a full blown object.

And finally, a specification for a given situation, which “is a” InviteUserProcessSpec, looks like this. Notice that I setup for the defined situation in the TestFixtureSetup. It’s here where I actually make the “Given” true.

[TestFixture]
public class Given_a_valid_email_address_and_a_valid_account_number : InviteUserProcessSpec
{
   [TestFixtureSetUp]
   public override void SetupTestFixture()
   {
      base.SetupTestFixture();
      validateEmailProcess.Stub( validateProc => validateProc.Execute( emailAddress ) ).Return( true ); // Given a valid email
      validateAcctProcess.Stub( validateProc => validateProc.Execute( accountNumber ) ).Return( true ); // and a valid account
      response = inviteUserProcess.Execute( emailAddress, accountNumber ); // For this spec, I can act here once for all asserts
   }

   [Test]
   public void The_process_should_pre_register_the_email_address_with_the_account()
   {
      preregisterEmailProcess.AssertWasCalled( registerProc => registerProc.Execute( emailAddress, accountNumber ) );
   }

   [Test]
   public void The_process_should_send_an_invitation_to_the_email_address()
   {
      sendInvitationProcess.AssertWasCalled( sendProc => sendProc.Execute( emailAddress ) );
   }

   [Test]
   public void The_process_should_return_successful()
   {
      Assert.AreEqual( InviteUserResponse.Successful, response ); // checking the returned enum value
   }
}

So this should look very similar to the bullet points from above. Notice how using the base class to hold the basics really cleans up the spec. I wouldn’t do this for just one situation, but we’ve got a handful of situations here. I’ll do one more situation for illustration.

[TestFixture]
public class Given_an_invalid_email_address_and_a_valid_account_number : InviteUserProcessSpec
{
   [TestFixtureSetUp]
   public override void SetupTestFixture()
   {
      base.SetupTestFixture();
      validateEmailProcess.Stub( validateProc => validateProc.Execute( emailAddress ) ).Return( false ); // Given an INvalid email
      validateAcctProcess.Stub( validateProc => validateProc.Execute( accountNumber ) ).Return( true ); // and a valid account
      response = inviteUserProcess.Execute( emailAddress, accountNumber );
   }

   [Test]
   public void The_process_should_not_pre_register_anything()
   {
      preregisterEmailProcess.AssertWasNotCalled( registerProc => registerProc.Execute( Arg<String>.Is.Anything, Arg<String>.Is.Anything ) );
   }

   [Test]
   public void The_process_should_not_send_an_invitation()
   {
      sendInvitationProcess.AssertWasNotCalled( sendProc => sendProc.Execute( Arg<String>.Is.Anything ) );
   }

   [Test]
   public void The_process_should_return_invalid_email()
   {
      Assert.AreEqual( InviteUserResponse.InvalidEmail, response );
   }
}

You can see that for the negative assertions (the process should not…) I’m using NUnit’s Arg<> class. Since this post is about the inheritance pattern and not NUnit syntax, I’ll just say that this guy can be very powerful.

So there it is, multiple situations tamed with inheritance.  And as far as that pattern goes, my big take away is noticing the leanness of the spec classes, including how the individual [Test] methods are just one assertion line. When I can easily get away with that, I do it. Sometimes I can’t, so I “act” within the [Test] method. When I see that the spec classes are simple, then I feel like the system code is getting well written, meaning that it’s understandable, it’s maintainable, it’s delectable! Or so I think today. “Therefore let him who thinks he stands take heed lest he fall.” So I’ll see what tomorrow holds. 🙂

Advertisements

4 Responses to Unit test formats, Episode II : Inheritance strikes back

  1. You know, that’s pretty similar to how I’m testing now – if I were to add anything, I’d mention that I’m now using the Shouldly library for my assertions (see http://shouldly.github.com/). It supports at least NUnit and, if nothing else, it is shorter to type than Assert.AreEqual or Assert.That(…, Is.Equal()).

    I like the idea of some of the other spec libraries, but I feel like they’re sometimes a little too obtuse. If you can’t immediately read the test to discover intent, then the test can be improved.

  2. Kaelin says:

    Shouldly looks nice; I might give it a try. I really think we could use a whole language devoted to unit testing. We may have come about as far as we can trying to twist and bend C# syntax into English sentences without getting a bit silly. Always good to hear your thoughts. Thanks.

  3. Pingback: Unit test patterns, Episode I : A foundation emerges « FullRewrite

  4. Pingback: Unit test patterns, Episode III : Avoid the combinatorial – cheat! « FullRewrite

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

%d bloggers like this: