CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Ian Cooper [MVP]


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.

 



Comments

TDD and Hard To Test Areas, Part 2 « 2Here's, It’s, Read, Part, Areas, Hard, Test, Cooper « Fashion News said:

Pingback from  TDD and Hard To Test Areas, Part 2 &laquo; 2Here's, It&rsquo;s, Read, Part, Areas, Hard, Test, Cooper &laquo; Fashion News

# September 10, 2008 5:51 PM

Kevin Pang said:

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

# September 10, 2008 7:18 PM

2008 September 11 - Links for today « My (almost) Daily Links said:

Pingback from  2008 September 11 - Links for today &laquo; My (almost) Daily Links

# September 11, 2008 1:02 AM

Reflective Perspective - Chris Alcock » The Morning Brew #177 said:

Pingback from  Reflective Perspective - Chris Alcock  &raquo; The Morning Brew #177

# September 11, 2008 3:36 AM

Colin Jack said:

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?

# September 11, 2008 8:15 AM

Dew Drop - September 11, 2008 | Alvin Ashcraft's Morning Dew said:

Pingback from  Dew Drop - September 11, 2008 | Alvin Ashcraft's Morning Dew

# September 11, 2008 9:10 AM

p90x said:

Quick Question: Regarding the end date of the 24th, will we need to post our final weight on Christmas day? I should be in town, but I’ m just worried I may forget to get on and post it on the 25th. May help everyone what with all the excitement and different

# October 6, 2008 12:02 AM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add
Check out Devlicio.us!

Our Sponsors