Blog Series: TDD and Process, Part 4

April 17, 2017 — Posted by Scott Bain

Part 4: Redundancy

In part 3, we examined how the same specification can be bound to the production system in different ways, producing executable tests that have various levels of granularity and speed, by creating different bindings for different purposes.  We started with the “full stack” binding:

Full-Stack Binding

Next, we created a different binding that avoided accessing the database at all by mocking out the persistence layer:

Mocked Persistence Layer

The idea is that you can create multiple bindings, each which has value in a different way.  Bindings that access only one layer (which I showed an example of too) are fast and give lots of specific information when they fail.  Bindings that access multiple layers are progressively slower (and therefore will be run less frequently) and will be increasingly vague, when they fail, as to the cause of the failure.  But they are also more comprehensive and ensure that the system components are properly integrated.  We want all these kinds of tests, and multiple bindings make this possible.

The specification will not be redundant, because we are reusing it (it its very readable form).  But the bindings will contain redundancies unless we do something about it. 

Remember this specification?

Feature: Sales Calculator

Scenario: Applying Sales Commission

       Given A base sale amount of $200.00

       And A commission rate of 8%

       When The total sale is calculated

       Then The total sale is $216.00

Let’s look at t a little C# code (largely pseudocode) to bind it to the full stack:

[Binding]
class SalesCalculatorStepsFullStack {
    [Given(@"A base sale amount of \$(.*)")]
    public void GivenABaseSaleAmountOf(double aSaleAmount) {
        // Code that brings up the system, enters the saleAmount
        // through the GUI, then
        // Code that activates the real persistence layer
        // and connects the system to it
    }

    [Given(@"A commission rate of (.*)%")]
    public void GivenACommissionRateOf(int aComissionRate) {
        // Code that enters the commissionRate into the GUI
    }

    [When(@"The total sale is calculated")]
    public void WhenTheTotalSaleIsCalculated() {
        // Code that presses the correct button in the GUI
    }

    [Then(@"The total sale is \$(.*)")]
    public void ThenTheTotalSaleIs(double expectedSale) {
        double actualSale = 0.00;
        // Code that retrieves the actualSale from the database
        Assert.AreEqual<double>(actualSale, expectedSale);
    }
}

If we write a binding that does not access the real persistence layer, but rather mocks it out, the code would be largely identical (differences are in bold):

[Binding]
class SalesCalculatorStepsFullStack {
    [Given(@"A base sale amount of \$(.*)")]
    public void GivenABaseSaleAmountOf(double aSaleAmount) {
        // Code that brings up the system, enters the saleAmount
        // through the GUI, then
        // Code that activates the mock persistence layer
        // and connects the system to it
    }

    [Given(@"A commission rate of (.*)%")]
    public void GivenACommissionRateOf(int aComissionRate) {
        // Code that enters the commissionRate into the GUI
    }

    [When(@"The total sale is calculated")]
    public void WhenTheTotalSaleIsCalculated() {
        // Code that presses the correct button in the GUI
    }

    [Then(@"The total sale is \$(.*)")]
    public void ThenTheTotalSaleIs(double expectedSale)  {
        double actualSale = 0.00;
        // Code that retrieves the actualSale from the mock 
        // persistence layer
        Assert.AreEqual<double>(actualSale, expectedSale);
    }
}

As you create various bindings this problem will become increasingly problematic, and when changes are made to something that affects them all then maintenance will get very laborious and thorny, things will be missed, etc… the wages of redundancy that we all know about[1].

A Design Problem

This is a design problem, and so of course the solution is to improve the design, in this case, of the bindings.  Not surprisingly we will use a pattern to do it.

Template Method Pattern

If you are familiar with the patterns you probably recognize this as a quite common form of the Template Method Pattern[2].  The two abstract methods in the base class are implemented differently.  In the Full Stack class the CreatePersistenceLayer() method creates the real this.  In the Mock Persistence class this same method creates the mock version.  Similarly, the RetrieveActualValue() in the first class access the database, while in the second class it accesses the in-memory mock.

Everything else is implemented in the base class, and thus the redundancy is eliminated.  As we continue to create more bindings we can capitalize on this design, extending and refactoring is as we go.  Side note, the base class would not be marked with the [Binding] attribute as it is abstract that thus not executable.  Both the derived classes are marked as bindings

This is only one example, and a fairly simple one.  I wanted to get across the basic ideas in blog form. quickly.  To get into the nitty-gritty details of this takes our hands-on ATDD Automation Lab[3], but I think you can at least get started with what I have provided here.

Conclusions

Hopefully you’ve found this useful.  My goal was to show you that you do not have to live with what I called the “false dichotomy” of acceptance test automation.  You can have fast, granular tests for your developers and larger, slower tests that ensure the comprehensive correctness of the entire system.  Also, that you can do this in a form that can be understood, edited, and even written by anyone who’s had some training in ATDD; business people, testers, developers, everyone.  Finally, I hope I’ve shown that it is possible to do all of this in a very sustainable way by, for example, eliminating any redundancies through quality design.

Feedback is welcome, as always.




[1] ..or if not, you can read about it here: http://portal.netobjectives.com/topic/concision/

[3] Contact us at http://www.netobjectives.com if you’re interested.  It’s a lab that accompanies our Acceptance Test-Driven Development training: http://www.netobjectives.com/training/acceptance-test-driven-development

 

Subscribe to our blog Net Objectives Thoughts Blog

Share this:

About the author | Scott Bain

Scott Bain is an consultant, trainer, and author who specializes in Test-Driven Development, Design Patterns, and Emergent Design.



        

Blog Authors

Al Shalloway
Business, Operations, Process, Sales, Agile Design and Patterns, Personal Development, Agile, Lean, SAFe, Kanban, Kanban Method, Scrum, Scrumban, XP
Cory Foy
Change Management, Innovation Games, Team Agility, Transitioning to Agile
Guy Beaver
Business and Strategy Development, Executive Management, Management, Operations, DevOps, Planning/Estimation, Change Management, Lean Implementation, Transitioning to Agile, Lean-Agile, Lean, SAFe, Kanban, Scrum
Israel Gat
Business and Strategy Development, DevOps, Lean Implementation, Agile, Lean, Kanban, Scrum
Jim Trott
Business and Strategy Development, Analysis and Design Methods, Change Management, Knowledge Management, Lean Implementation, Team Agility, Transitioning to Agile, Workflow, Technical Writing, Certifications, Coaching, Mentoring, Online Training, Professional Development, Agile, Lean-Agile, SAFe, Kanban
Ken Pugh
Agile Design and Patterns, Software Design, Design Patterns, C++, C#, Java, Technical Writing, TDD, ATDD, Certifications, Coaching, Mentoring, Professional Development, Agile, Lean-Agile, Lean, SAFe, Kanban, Kanban Method, Scrum, Scrumban, XP
Marc Danziger
Business and Strategy Development, Change Management, Team Agility, Online Communities, Promotional Initiatives, Sales and Marketing Collateral
Max Guernsey
Analysis and Design Methods, Planning/Estimation, Database Agility, Design Patterns, TDD, TDD Databases, ATDD, Lean-Agile, Scrum
Scott Bain
Analysis and Design Methods, Agile Design and Patterns, Software Design, Design Patterns, Technical Writing, TDD, Coaching, Mentoring, Online Training, Professional Development, Agile
Steve Thomas
Business and Strategy Development, Change Management, Lean Implementation, Team Agility, Transitioning to Agile
Tom Grant
Business and Strategy Development, Executive Management, Management, DevOps, Analyst, Analysis and Design Methods, Planning/Estimation, Innovation Games, Lean Implementation, Agile, Lean-Agile, Lean, Kanban