TDD and Hard To Test Areas, Part 2

It’s been a while. I have been heads down on a new project (more about that some other time), and have not got around to posting. Without further ado here is the follow up post to TDD and Hart To Test Areas, Part 1

Depend upon Abstractions

The Gang of Four’s first principle is to program against
abstractions not implementations. If we use abstractions then we can solve the
hard-to-test problem by implementing the abstraction in terms of the
hard-to-test dependency in production, but with a simple-to-test dependency in
test.  So we could use an IDatabase to
abstract out our interaction with the Db, using concrete Ado.Net classes in
production, but replace it with an in-memory collection for testing. Jeremy
Miller summarizes this approach as ‘isolate
the ugly stuff
’.

We need to show some rationality here. No one wants IString,
everyone wants IDatabase, and we probably don’t need an ICustomer, but we
might. The advantage of TDD is in flushing out where ICustomer is useful by
exercising the SUT.  So when designing for testability we need to
think about which dependencies we want to allow to be concrete, and which won’t
don’t. Different schools of programming make different value judgments here.
Classicist approaches tend to avoid replacing all dependencies, focusing
instead on ones that are needful to support extensibility and layering.

Design principles should help us identify when to use
interfaces and these tend provide the opportunities we need to use abstractions
to isolate hard-to-test code.

Layers

A layered architecture also creates a need for abstractions.
While the layers must communicate, like a layer cake higher lays may depend on
lower-layers but not vice-versa. To effect this, higher layers in our
architecture should depend on an abstraction exposed by the lower layer, not a
concrete type. The two layers can communicate via the agreed contract, but the
higher layer has no dependency on the lower layer. Robert Martin’s Dependency Inversion
Principle states that High level modules
should not depend upon low level modules. Both should depend upon abstractions.
Abstractions should not depend upon details. Details should depend upon
abstractions.

This dovetails with hard-to-test areas as layer boundaries
often co-inside with hard-to-test areas such as the UI or access to external
systems. So using abstractions when we layer helps us to achieve testability. To
ensure cohesion we often talk to a façade when we cross a layer boundary, which
hides the complexity of the other layer, and this again simplifies testing, by removing
the need to create the objects that implement the façade as part of our test
setup.

Open-Closed Principle

In Agile Principles, Patterns, and Practices in C# Robert
Martin describes the Open-Closed Principle as “Software entities (classes, modules, functions, etc.) should be open
for extension but closed for modification.

To achieve this ‘impossible thing before breakfast’ we use
polymorphism. By creating an abstraction (either explicitly by using an
interface or abstract base class, or implicitly by marking methods as virtual),
we both define a contract allows us to define how we interact with that type -
it is closed for modification – but allows many concrete implementations of
that type –  we are open for extension.
Note that an abstract type

Seams

The extension points provided by abstractions are ideal for testing as they
allow us to replace depended upon components for testing. Michael Feathers
calls these points seams: A seam is a
place where you can alter behavior in your program without editing in that
place
. A virtual method can be especially useful in legacy code to
create a point of extensibility to test code, where we cannot reasonably extract or introduce an interface to allow us to do so. 

Test Doubles

The concept of replacing a hard-to-test implementation with
an easier to test one was originally called mocking. This term has become
overloaded so Gerard Meszaros has suggested replacing it with the term Test
Double of which a Mock is only one category. The term Test Double is meant to
convey the idea of the Stunt Double who replaces the actor for dangerous
scenes. There are two different scenarios in which we want to use a
Test Double.

  • Where we need to control the inputs into a SUT, but there is
    no observation point for us to do so. For example, if we access a repository in
    our SUT to return an entity from the Db, such as a Customer, so that we can
    retrieve some value from the Customer, we need to have some way of controlling
    what is returned from our repository as that forms an input into the SUT.
  • Where we need to monitor the outputs from a SUT, but there
    is no observation point for us to do so. For example if we are calling an
    external web service from our SUT then we want to monitor the arguments sent to
    that service, but we don’t want to actually make the call.

Working with Indirect Inputs

Controlling the inputs from the Depended On Component (DOC) to the System Under Test (SUT) is needed when because we want to control the flow of execution within the
SUT, but we do not care about the outputs to that DOC. We may want to control
execution to allow the ‘happy path’ but we might want to control execution to
test the error path too, by providing appropriate input.

The need to control indirect inputs occurs
because where we have difficulty setting up the values returned by the DOC to
the SUT. Often this occurs because the DOC is in hard-to-test area (Db, file
system, across network or process/AppDomain boundary). Such a DOC should
generally be represented by an abstraction – depend upon abstractions – we have
seam for our test in replacing the ‘ugly stuff’ in the DOC with a test double.

There are two patterns for replacing a DOC
when we need to work with indirect input: stub and fake

Stub

A stub is a light-weight implementation of
an interface. With a stub we just want it to return some values in response to
a method call or property access on the DOC that allows the method on the SUT
to continue processing. A lot of ‘mocking’ frameworks now have first class
support for stubs.

In the following example an Insurance
Product is our SUT which uses the DOC of a rating service and asks the DOC for
the version of the rating plan that is in force. Here we are using Rhino Mocks
support for automatically generating a stub.

    [TestClass]

    public class Using_A_Stub_To_Replace_A_Depended_Upon_Component

    {

        public IRatingService
ratingService;

        [TestInitialize]

        public void
Should_Use_A_Stub_To_Replace_Rating_Service()

        {

           
ratingService = MockRepository.GenerateStub<IRatingService>();

        }

 

        [TestMethod]

        public void
Should_Return_The_Version_Of_The_Product_Being_Used()

        {

            //setup

            Product product = new
Product(ratingService);

            const int
RATING_PLAN_VERSION = 5;

           
ratingService.RatingPlanVersion = RATING_PLAN_VERSION;

 

            //exercise

            int ratingPlanVersion =
product.GetRatingPlanVersion();

 

            //verify

            Assert.AreEqual(RATING_PLAN_VERSION,
ratingPlanVersion, “Expected the rating plan
to be the one from the rating service”
);

        }

    }

    public class Product

    {

        public IRatingService
ratingService;

        public Product()

        {

           
ratingService = null;

        }

        public Product(IRatingService
ratingService)

        {

           this.ratingService = ratingService;

        }

        public int
GetRatingPlanVersion()

        {

           return
ratingService.RatingPlanVersion;

        }

    }

We use a test stub where we need to control
the indirect inputs from a DOC to the SUT, but we do not need to verify the
indirect outputs. A stub can be useful to force execution of certain paths in
the SUT, by returning values that direct the SUT down that path. This can be
useful for error testing.

Fake

A stub returns a value to the SUT from the
DOC, but cannot stand in place of the DOC. It is often incomplete and usable
only in the context it was set up for. A fake is in contrast is a lightweight
version of the DOC. It behaves in a similar fashion to the original. We
commonly use a fake where we have multiple interactions with the DOC in the SUT
i.e. load and retrieve or because we need a test double across a number of test
fixtures and we want to remove duplicate stubs. Fake or in-memory database and
fake web service are common uses.

In this example we want to replace a
service that we depend upon that manages endorsements, with a fake. Whereas the
stub is dumb the fake has the semantic of the DOC. The FindEndorsements method
on the fake loops through its collection of endorsements and returns those that
match the required type. However, while our production implementation might
source its list of endorsements from a Db, our fake just uses an in-memory
collection. This isolates us from an y Db issues like shared fixture or slow
tests.

    [TestClass]

    public class Using_A_Fake_To_Replace_A_Depended_Upon_Conponent

    {

        IEndorsementService endorsementService;

       

        [TestInitialize]

        public void
Create_A_Fake_For_The_Endorsement_Service()

        {

           
endorsementService = new FakeEndorsementService();

        }

        [TestMethod]

        public void
Should_Return_Count_Of_Mandatory_Endorsements()

        {

           //setup

            var product = new Product(endorsementService);

            const int
NO_OF_MANDATORY_ENDORSEMENTS = 12;

           
endorsementService.Endorsements.AddRange(CreateMandatoryEndorsementList(NO_OF_MANDATORY_ENDORSEMENTS));

            const int
NO_OF_OPTIONAL_ENDORSEMENTS = 2;

           
endorsementService.Endorsements.AddRange(CreateOptionalEndorsementList(NO_OF_OPTIONAL_ENDORSEMENTS));

 

           //exercise

            var
mandatoryEndorsements = product.FindMandatoryEndorsements();

 

            //verify

            Assert.AreEqual(12, mandatoryEndorsements.Count, “Expected the number of mandatory endorsements to
equal those added”
) ;

        }

        private List<Endorsement> CreateEndorsementList(int noOfEndorsements, EndorsementType
type)

        {

            List<Endorsement>
endorsements = new List<Endorsement>();

            for (int i = 0; i
< noOfEndorsements; i++)

            {

               
endorsements.Add(new Endorsement(type));

            }

            return endorsements;

        }

        private List<Endorsement> CreateMandatoryEndorsementList(int noOfEndorsements)

        {

            return CreateEndorsementList(noOfEndorsements, EndorsementType.Mandatory);

        }

        private IEnumerable<Endorsement> CreateOptionalEndorsementList(int noOfEndorsements)

        {

            return CreateEndorsementList(noOfEndorsements, EndorsementType.Optional);

        }

}

 

public class Product

{

        public IEndorsementService
endorsementService;

        int productId;

        public IRatingService
ratingService;

 

        public Product()

        {

           
ratingService = null;

        }

 

        public Product(IRatingService
ratingService)

        {

           this.ratingService = ratingService;

        }

 

        public Product(IEndorsementService
endorsementService)

        {

            this.endorsementService = endorsementService;

        }

 

        public List<Endorsement> FindMandatoryEndorsements()

        {

            return endorsementService.FindEndorsements(productId,
EndorsementType.Mandatory);

        }

 

        public int
GetRatingPlanVersion()

        {

           return ratingService.RatingPlanVersion;

        }

 }

    public class FakeEndorsementService : IEndorsementService

    {

        private List<Endorsement> endorsements;

        public List<Endorsement> Endorsements

        {

            get

            {

                return endorsements;

            }

            set

            {

               
endorsements = value;

            }

        }

 

        public List<Endorsement> FindEndorsements(int productId, EndorsementType
endorsementType)

        {

            List<Endorsement>
matchingEndorsements = new List<Endorsement>();

            foreach (Endorsement
endorsement in endorsements)

            {

                if (endorsement.Type == endorsementType)

                   
matchingEndorsements.Add(endorsement);

            }

            return matchingEndorsements;

        }

     }

 

Checking on Indirect Outputs

We need to check outputs where what we send
to the DOC needs to be tested. Usually this occurs because we have no means to
retrieve the message that was sent to the DOC by the SUT in the verification
phase of our test, but need to verify that message to ensure that our code
works correctly. In other words we are not testing a change in state to our
SUT, but a message sent to a DOC.

When we need to record the messages from
the SUT to the DOC there are two options: a spy and a mock.

Spy

A spy allows us to record the changed state
of the DOC and then use an assertion to confirm that state within our test.

As such it dovetails well with the normal
test model. A spy is especially valuable where we do cannot predict all of the
values passed to the DOC ahead of time, and instead need to confirm them, often
against the state of the SUT when the test has finished exercising the code.

A self-shunt can be a quick way to
implement a spy. The test fixture implements the interface for the DOC and
records any interactions in fields on the class. Confirmation then involves
testing the fields. Be aware that shared fixture state means that you need to
use an explicit setup to clear that shared state if more than one test uses the
same fixture.

In the following test we check the document
job passed to the document service when we bind a document. Within the demo we
are just checking for the existence of the job, but in a real test we could
examine the job to determine if it met our expectations of what should be
generated.

    [TestClass]

    public class Use_A_Test_Spy_To_Replace_A_DOC : IDocumentService

    {

        public DocumentJob
job;

        [TestMethod]

        public void
Should_Call_Document_Service_With_Document_Job()

        {

            //setup

            var submission = new Submission(this);

            //exercise

           
submission.GenerateQuoteLetter();

            //verify – a real test would do more testing around what the
job contains!

            Assert.IsNotNull(job);

        }

 

        public void
FufillDocumentRequest(DocumentJob job)

        {

            this.job = job;

        }

     }

 

   public class DocumentJob

    {

    }

 

    public class Submission

    {

        private IDocumentService service;

 

        public Submission(IDocumentService
service)

        {

            this.service = service;

        }

 

        public void
GenerateQuoteLetter()

        {

            DocumentJob job = new
DocumentJob();

            service.FufillDocumentRequest(job);

        }

    }

 

Mock

A mock is useful when we want to confirm
the sequence and number of messages to the DOC, specifically the method calls
and the arguments passed to them.  A mock
is intrusive in that it specifies the interaction that the SUT should have with
the DOC and confirms that the interaction meets that specification. It is thus
less amenable to refactoring because it has knowledge of the implementation of
the SUT.

Mocks tend to be implemented using frameworks,
so that makes them cheap to write, but the problem here can be a temptation to use
mocks out of their appropriate context just because we have a framework for
them. The issue here is we can end up with over-specified software that is
difficult to refactor, because the tests lock in our implementation choices.

Using ‘loose replay’ semantics, which allow
mocks to respond without raising an error if calls are not made, can reduce the
coupling between specification and implementation where appropriate.

A mock differs from a spy in that a mock
forces us to specify the expected interaction before we exercise the SUT, not
after. It may be better to use a spy where you are
uncomfortable with the test specifying the implementation of the SUT.

 In this, contrived, example we call an
external OFAC service to search for our customer. We don’t return the result,
but set a flag which is used in later rules. We want to test our output to the
OFAC service, which is third-party to confirm that our interaction is correct.

     [TestClass]

    public class Use_A_Mock_To_Replace_A_DOC

    {

        private IOFACService
oFACService;

        private MockRepository
mocks = new MockRepository();

 

        [TestInitialize]

        public void
Create_A_Mock_OFAC_Service()

        {

           
oFACService = mocks.CreateMock<IOFACService>();
//strict replay semantics

        }

       

        [TestMethod]

        public void
Should_Call_OFAC_Service_When_Validating_Client()

        {

            //setup

            Account newClient = new
Account();

           
newClient.FirstName = “Ian”;

           
newClient.Surname = “Cooper”;

           

            using(mocks.Record())

            {

                Expect.Call(oFACService.SearchSDNList(newClient.ToString())).Return(true);

            }

 

            //exercise

            using (mocks.Playback())

            {

               
newClient.KnowTheCustomer(oFACService);   

            } //verify

         }

    }

 

    public class Account

    {

        private bool onWatchList;

        public string
FirstName {get;set;}

        public string Surname
{get;set;}

 

        public void
KnowTheCustomer(IOFACService oFACService)

        {

           
onWatchList = oFACService.SearchSDNList(ToString());

        }

 

        public override string ToString()

        {

            return FirstName +
+ Surname;

        }

 

    }

 

Inversion of Control

Note that in order to replace the DOC with
a stub; we must support Dependency Injection i.e. the instance of the DOC used
by the DOC must be supplied to it.

We could do this directly in our code;
before we create the object we create its dependencies. The problem here is
duplicating all that code to create our dependencies.

We could create a service locater that we
can ask for instances of common dependencies. This removes the duplicate code,
and allows us to, for example, provide different implementations of the DOC but
creates an additional dependency for the class, on the Service Locater.

Finally we could use an Inversion of Control
(IOC) container. With an IOC container we ask the container for the finished
object. Inversion of Control is sometimes called the Hollywood Principle or
“don’t call us, we’ call you”. We are using IOC in this context because we no
longer have a dependency on a Service Locater, but hand off to an assembler and
ask it to inject our dependencies. The framework creates us instead of us
creating using the framework. Using an Inversion of Control container, such as
Windsor, simplifies the creation of objects with their required dependencies.

 

About Ian Cooper

Ian Cooper has over 18 years of experience delivering Microsoft platform solutions in government, healthcare, and finance. During that time he has worked for the DTi, Reuters, Sungard, Misys and Beazley delivering everything from bespoke enterpise solutions to 'shrink-wrapped' products to thousands of customers. Ian is a passionate exponent of the benefits of OO and Agile. He is test-infected and contagious. When he is not writing C# code he is also the and founder of the London .NET user group. http://www.dnug.org.uk
This entry was posted in Agile, Fakes, Mocks, Object-Orientation, Stubs, TDD, xUnit. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://colinjack.blogspot.com Colin Jack

    As always really interesting content.

    “To effect this, higher layers in our architecture should depend on an abstraction exposed by the lower layer, not a concrete type.”

    I’ve always taken the GUI to be a lower level layer in these discussions, which I realize is slightly not standard way of looking at it.

    I partly base this on another of Rober Martin’s views which I think boils down to “keep the high level policy independant of the lower level details”. With that in mind I’m totally stringent about ensuring that any important logic (business/process/workflow etc) is kept isolated from implementation details including the GUI/database/distribution.

    Of course stable abstractions principle might make us realize that our domain is ending up being very stable but in that case a presentation model (with a facade) approach might be the better solution.

    What do you think?

  • http://www.kevinwilliampang.com Kevin Pang

    Great summary of the benefits of TDD and the benefits of IOC.