Dennis Burton's Develop Using .NET

Change is optional. Survival is not required.
Tags: patterns | programming | testing

The Scenario

On our continuing quest to create unit tests that exercise only the class under test, we look at another common scenario that occurs while writing tests. As with the Dummy, our class under test has a dependency on another class, only this time the dependent class has an active role in our test. Our testing needs are about the logic of the class under test and not the interaction with the dependent object. In order to create a good test, the class under test must be isolated from the dependent object.

An example of this scenario might look like this:

   11     public interface ICalculator

   12     {

   13         int Add(int left, int right);

   14     }

   15     public class Fib

   16     {

   17         private ICalculator _calculator;

   18         public ICalculator Calculator

   19         {

   20             get { return _calculator; }

   21             set { _calculator = value;    }

   22         }

   23 

   24         public int Next(int i, int j)

   25         { return Calculator.Add(i,j); }

   26     }

The class under test in this scenario is Fib, which has a dependency on an ICalculator. The test's objective is to validate that the Next method returns the correct result for some well-known examples.

The Vocabulary

The name of this pattern is the Stub. A stub stands in the place of the actual object in use and provides known answers and predictable behavior. If you are doing any sort of evolutionary development, chances are that the initial versions of your classes more resemble stubs then real code. Why? The goal is the same: you wrote stub functionality to allow you to focus your development efforts on different parts of the system. This is exactly what we are doing with Stub tests: isolate one part of the system from another by providing known results.

The solution without tools

Unlike with a Dummy, providing a class that throws a NotImplementedExcpetion in the Add method does not meet our needs. Since the functionality of ICalculator is outside the scope of this test, we assume that it is working correctly (and hopefully under test). A simplistic implementation of ICalculator will work nicely. Since we are not testing the calculator, provide a simplistic calculator that returns fixed results.

   38     [Test]

   39     public void NextResultIsCorrect()

   40     {

   41         Fib fib = new Fib();

   42         fib.Calculator = new StubCalculator();

   43 

   44         Assert.That(fib.Next(2, 3), Is.EqualTo(5));

   45     }

 

   29     public class StubCalculator : ICalculator

   30     {

   31         public int Add(int left, int right)

   32         { return 5; }

   33     }

The test will check that the Next method returns the correct result given the StubCalculator. What we end up doing here is fully exercising the Fib class with known values from its dependent classes. The stub gives us the proper level of isolation for this test.

The solution with Rhino Mocks

For this version, leverage Rhino Mocks to keep us from having to code physical versions of the Stub class. Using Rhino's Fluent Interface, this reads as Expect a call on Calculator with the parameters 2 and 3, and when making this call return 5 as a result.

   47     [Test]

   48     public void NextNumberIsCorrect()

   49     {

   50         Fib fib = new Fib();

   51         fib.Calculator = MockRepository.GenerateStub<ICalculator>();

   52         fib.Calculator.Expect(calc => calc.Add(2, 3)).Return(5);

   53 

   54         Assert.That(fib.Next(2, 3), Is.EqualTo(5));

   55     }

The tools advantage

Just as discussed with IComplicated in the Dummy sample, adding methods to ICalculator does not require any additional maintenance of this test. However, unlike the Dummy sample, calls into the stub return the expected value. Additional benefits can easily pile up. Consider adding multiple calls to Add with different parameters. The hand-coded version would need some sort of conditional logic to determine what to return based on the calling parameters. Complexity adds up fast, even in the simple example listed here. Using Rhino, one concise and readable line of code can use new parameters for an additional expectation, including the expected result, and Rhino deals with matching up the parameters with the correct result. This is just a glimpse into the functionality offered by Rhino; the upcoming patterns will cover even more capability.

Isolation effects

Should a test that checks the result of the Next method fail if there is something wrong with the ICalculator implementation? The answer, as always, is "it depends." This test should fail if we were writing an integration test—a test that ensures all the pieces of a system are working together. This test should not fail based on ICalculator if it is a unit test, focused only on the result of Next. Isolating ICalculator from Fib helps build a set of unit tests that can quickly identify the location of errors introduced into a system. The stub is a common pattern of isolation, and using it will make a marked and immediate improvement in your tests.

The Series

PatternsInTesting[4] - Mock Pattern
Tags: patterns | programming | testing

The Scenario

One of the characteristics of a good unit test is that the object under test is the only object being exercised. The problem in this scenario is that the object under test requires a dependent object, even though the functionality of the dependent object is not used in the test. To make a good test, the dependent object needs to be isolated from the class under test.

An example of this scenario might look like this:

    8     public interface IAmComplicated

    9     { void DoStuff(); }

   10 

   11     public class ClassUnderTest

   12     {

   13         private IAmComplicated complicated;

   14         public double circumference;

   15         public double radius;

   16         public ClassUnderTest(IAmComplicated externalComplicated)

   17         {

   18             if( externalComplicated == null )

   19                 throw new ArgumentNullException("complicated is required");

   20             complicated = externalComplicated;

   21         }

   22         public double DoInternalStuff()

   23         {

   24             return circumference/(2*radius);

   25         }

   26     }

The dependency in ClassUnderTest is that its constructor requires an instance of an object that implements IAmComplicated. The rather simple objective of this test is to validate that DoInternalStuff returns Pi within a reasonable amount of rounding error.

The Vocabulary

The name of this pattern is the Dummy. It is unclear to me whether this is a reference to the object that enables isolation for the test or a reference to the original author of the code. It seems to be a code smell for this scenario to even occur. However, sometimes you need to use an external library and do not have liberty of changing the code.

The solution without tools

Using only Visual Studio, add a class that implements IAmComplicated, right-click on it, and choose implement interface. Every method in the class will throw a NotImplementedException. This class meets the needs of the test because none of the methods are ever called; its only purpose is existence.This is your Dummy.

    1     public class ComplicatedDummy : IAmComplicated

    2     {

    3         public void DoStuff()

    4         {

    5             throw new NotImplementedException();

    6         }

    7     }

This Dummy class allows us to create the following test:

   39     [Test]

   40     public void DoStuffValidates()

   41     {

   42         ClassUnderTest cut = new ClassUnderTest(new ComplicatedDummy());

   43         cut.circumference = 314;

   44         cut.radius = 50;

   45         Assert.AreEqual(Math.PI, cut.DoInternalStuff(), 0.01d);

   46     }

The solution with Rhino Mocks

Rhino Mocks has the capability to create an object at runtime by reflecting the IAmComplicated interface. This gives us the capability we need without having to maintain another class in the test project. Since Rhino is reflecting the interface at runtime, adding a method to the interface at a later date does not require changes to the test code. There are several different ways Rhino could give us a placeholder for IAmComplicated. Here we will use a simple one line call to GenerateStub.

   55     MockRepository.GenerateStub<IAmComplicated>();

This one line saves us an entire file of maintenance, and our test only requires minor modification to use this technique. Place the call to Rhino as the parameter to the ClassUnderTest constructor instead of creating a new ComplicatedDummy.

   52         [Test]

   53         public void DoStuffValidates()       {

   55             ClassUnderTest cut = new ClassUnderTest(MockRepository.GenerateStub<IAmComplicated>());

   56             cut.circumference = 314;

   57             cut.radius = 50;

   58             Assert.AreEqual(Math.PI, cut.DoInternalStuff(), 0.01d);

   59         }      

Isolation of Failure

One of the primary goals of good unit tests is to identify exactly which unit caused the error. By applying this pattern, tests that cover ClassUnderTest no longer require an implementation of IAmComplicated to instantiate without failure. This helps shorten your bug hunting cycle by reducing the areas that indicate error to where the real error occurred. As you attain higher levels of isolation and better definition of units, you will find you spend much less time in the debugger. This means spending less time finding problems more time fixing them.

The Series

PatternsInTesting[2] - Stub Pattern PatternsInTesting[4] - Mock Pattern
Tags: patterns | programming | testing

It does not matter if your test philosophy is Test Driven Development, Test Eventually Development, or I write tests occasionally. Very early in your testing experience, you will want to start isolating portions of your application or libraries. Without this isolation, you will start seeing a fragile set of tests that make it challenging to track down the point of failure. In order to reach the level of isolation that your tests need, you will need to introduce Mocks (or Test Doubles) to your testing toolbox.

The Testing Story

Someone on your team (extra geek cred if it was you) brings the concept of adding tests to the development process. No doubt there is some confusion at first. What are tests? How do we write them? This phase quickly passes as the team finds things that they understand how to test. This initial period of enlightenment yields great results. Everyone is happy that code is doing what it should be doing. Occasionally, the team celebrates when a broken test flags an unintended change. Clearly development will never be the same again.

As time goes on and the test library gets larger, you start to notice that a breaking change often causes quite a few tests to fail. The team finds this annoying, but decides having tests is better than developer life before tests. At some point though, the tests start to feel burdensome. Adding simple changes start to propagate through the whole system of tests. Discouragement and discontentment set in, and some of your team members want to abandon developing with tests.

Enter Mocks

At this point, the prominent options are to ignore the tests (the blue pill) or find a way to make the tests better (the red pill). This series will cover some of the test development patterns used to isolate areas of your code in the hopes that you will choose the red pill. The goal for the initial four or five posts will be to cover the types of scenarios that occur in test code and what common solutions look like. After that, I hope to cover some of the patterns for dealing with legacy code as I am currently reading through Working Effectively with Legacy Code and it seems relevant to the client I am working with.

The Mock Controversy

There has been considerable conversation in the community about the usage of the term mock. Some prominent members of the development community have been pushing for changing the vocabulary to test doubles, where mock, stub, fake and spy are a particular type of test doubles. My take on this is that these test double types are each describing the scenario where it is useful and the technique for solving the problem; this is describing a pattern. Good names are the key to effective patterns, and we have a good set of names for pattern description. It really does not matter what you call the concept (mocking | doubling | faking | test doubles) if the pattern names describe the scenario effectively.

What's next?

This Series of posts will explore these test double patterns and decompose a few alternate implementations.

The Series

PatternsInTesting[4] - Mock Pattern

Dennis Burton

View Dennis Burton's profile on LinkedIn
Follow me on twitter
Rate my presentations
Google Code repository

Community Events

Windows Azure Boot Camp Lansing GiveCamp