Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

TDD with Loops and Aggregated Operations

This was originally meant to be an example in the Test Small Before Testing Big post.  Several months ago I overheard a developer pair on the other side of the room struggling over how to write a specific unit test.  When I wandered over to take a look the cause of their distress was easily apparent.  They were coding a testing harness that would sweep a two deep hierarchy of folders and perform a set of actions on each file the code found in each folder and subfolder.  They ran into trouble because they were coding from the top down.  The initial test was going to run from the entry point of the code all the way down.  They bumped into a couple common issues:



  1. They simply bit off too much to chew in one unit test.  One of the most common causes of a difficult unit test is that it covers too much scope.  In their case they were trying to put together test data for the folder/file hierarchy and a set of expectations to verify possible outcomes of each file.

  2. Far too much test data setup.  Copious amounts of test data setup is a code smell (read this section on the original Wiki).  By and large, if you feel like a unit test is dominated by mock object setup, it’s usually time to split the code into smaller chunks.

In this very common development scenario, I think the most important thing for a TDD practitioner to remember is to “code from the bottom up.”  In my colleague’s case, they followed my suggestion to test the actions on a single file first, then tested that the folder system iteration separately and began to more forward.  Divide and conquer.


I hit a similar situation last week in our current project that I think is a good example (and doesn’t require any real obfuscation).  I was coding a class called BundleTranslationRequestAgent that runs in our little messaging broker service to find new “Bundle’s” and try to send a message to an external web service, audit the activity, and persist the state of the Bundle objects.  Starting from the bottom, I wrote and tested the code to handle a single Bundle object.



        public virtual void ProcessBundle(Bundle bundle)


        {


            // Create the outgoing Data Transfer Object


            BundleMessage message = new BundleMessage();


            message.ControlNumber = bundle.Id;


 


            // BundleTranslationService is a proxy to an external web service


            BundleMessageResponse response = _translationService.PublishBundleBytes(message, bundle.PdfContents);


            if (response.Success)


            {


                bundle.LogSuccessfulSubmission();


            }


            else


            {


                bundle.LogSubmissionFailure(response.FailureMessage);


            }


 


            // Update the status


            _repository.Update(bundle);


        }


Working with just this small slice of the functionality, I wrote unit test cases for both success and failure cases and used mock objects to test the interaction from the agent class to the web service and the backing BundleRepository for persistence.  Here’s a sample of a “happy path” unit test that uses RhinoMocks to mock the underlying services:



        [SetUp]


        public void SetUp()


        {


            mocks = new MockRepository();


            repository = (IBundleRepository) mocks.CreateMock(typeof (IBundleRepository));


            translationService = (IBundleTranslationService) mocks.CreateMock(typeof (IBundleTranslationService));


 


            agent = new BundleTranslationRequestAgent(translationService, repository);


        }


Now that the class under test is set up with mock objects in place, run the actual test:



        [Test]


        public void ProcessHappyPath()


        {


            Bundle bundle = new Bundle(1234);


            bundle.PdfContents = new byte[] {1, 2, 3};


 


 


            BundleMessage message = new BundleMessage();


            message.ControlNumber = bundle.Id;


 


            BundleMessageResponse response = new BundleMessageResponse(bundle.Id, true);


 


            Expect.Call(translationService.PublishBundleBytes(message, null))


                .Return(response)


                .Constraints(new Equal(message),


                            new Equal(bundle.PdfContents));


 


            repository.Update(bundle);


 


            mocks.ReplayAll();


 


            agent.ProcessBundle(bundle);


 


            Assert.AreEqual(BundleStatus.Sent, bundle.Status);


            BundleAudit audit = bundle.GetLastAudit();


            Assert.AreEqual(BundleAudit.SUBMITTED, audit.Action);


 


            mocks.VerifyAll();


        }


Note that I made the ProcessBundle() method public and marked it virtual, more on this later.


The next step is to proceed to the looping operation.  In this case I need to test that the BundleTranslationRequestAgent makes the right query to the underlying BundleRepository object and processes each Bundle object that is returned from the repository.  There are some additional unit tests to check exception cases, i.e. the web service blows up.  I’ve already tested the processing of a single Bundle, so to simplify the unit testing of the aggregate operation I’d like to remove the single Bundle processing somehow.  The solution that I used in this case was to create a subclass of BundleTranslationRequestAgent that overrides the ProcessBundle() method with a mock method.



    public class PartialBundleTranslationRequestAgent : BundleTranslationRequestAgent


    {


        private ArrayList _bundles = new ArrayList();


 


        public PartialBundleTranslationRequestAgent(IBundleTranslationService translationService, IBundleRepository repository) : base(translationService, repository)


        {


        }


 


        public void ExpectBundles(Bundle[] bundles)


        {


            _bundles.AddRange(bundles);


        }


 


        public override void ProcessBundle(Bundle bundle)


        {


            _bundles.Remove(bundle);


        }


 


        public void AssertAllBundlesWereProcessed()


        {


            Assert.AreEqual(0, _bundles.Count);


        }


    }


Now, to unit test the aggregate operation I actually test against this new PartialBundleTranslationRequestAgent class with the mocked ProcessBundle() method.



        [Test]


        public void ExecuteHappyPath()


        {


            BundleQuery query = new BundleQuery();


            query.Status = BundleStatus.New;


 


            Bundle[] bundles = new Bundle[]{new Bundle(1), new Bundle(3), new Bundle(4), new Bundle(5)};


 


            translationService.TestAvailability();


 


            Expect.Call(repository.Query(query)).Return(bundles).Constraints(new Equal(query));


            mocks.ReplayAll();


 


            PartialBundleTranslationRequestAgent partialAgent = new PartialBundleTranslationRequestAgent(translationService, repository);


            partialAgent.ExpectBundles(bundles);


 


            partialAgent.Execute();


 


            mocks.VerifyAll();


            partialAgent.AssertAllBundlesWereProcessed();


        }


In this unit test I simply told the instance of PartialBundleTranslationRequestAgent which Bundle‘s should be processed, and asked it later in the unit test to verify that every single Bundle was passed into its ProcessBundle() method.


This wasn’t a very complex example, but even so, tackling the nested operation first before worrying about the aggregated operation kept each unit test small and easy to write.  One of the keys for frustration-free TDD is to learn how to create small unit tests that can be solved in rapid order.  If I had tried to write and unit test this code from the top down I could have easily gotten bogged down in much larger tests that would have been much harder to write and read.  One way or another, coding always comes down to “Divide and Conquer.”  You’ve just got to remember the “Divide” part of the equation.


Wrapping Up


The trick of overriding a single method to create a unit testing version of a class isn’t something I do very often, but it’s handy in spots like a looping operation.  If the individual operation is complex I would probably split that operation off into its own class and use a mock object in testing the aggregated operation instead.  In the Bundle example I thought the code was simple enough that I preferred to just keep the code altogether in one place.  Many people would be upset about the ProcessBundle() method being scoped as public even though its really an internal operation.  In this case the clients of the BundleTranslationRequestAgent are calling it through the IAgent interface that has only the single Execute() method, so I really don’t worry too much about the lack of a bit of encapsulation here.  My philosophy is to concentrate on making the code easier to test, and hence easier to complete.

About Jeremy Miller

Jeremy is the Chief Software Architect at Dovetail Software, the coolest ISV in Austin. Jeremy began his IT career writing "Shadow IT" applications to automate his engineering documentation, then wandered into software development because it looked like more fun. Jeremy is the author of the open source StructureMap tool for Dependency Injection with .Net, StoryTeller for supercharged acceptance testing in .Net, and one of the principal developers behind FubuMVC. Jeremy's thoughts on all things software can be found at The Shade Tree Developer at http://codebetter.com/jeremymiller.
This entry was posted in Test Driven Development. Bookmark the permalink. Follow any comments here with the RSS feed for this post.