Simple Pattern for Arrange-Act-Assert style of Tests

Came up with a nice little pattern to implement Arrange-Act-Assert (AAA) style of tests using Visual Studio’s MSTest last night. The pattern is ridiculously simple and can be very useful for writing outside-in style of tests (some time referred to as acceptance tests too). By outside-in tests I am referring to those high-level tests which are meant to test, from top to bottom, a slice of business functionality.

At the heart of this simple pattern is an abstract base class which forces the concrete test classes to follow the AAA. This base class declares abstract signatures for Arrange(), Act() and Assert() methods and requires all the concrete classes to implement them.

protected abstract void Arrange();
protected abstract void Act();
protected abstract void Assert();

Hence, any class that implements this base class will have to implement the following

Arrange – which will allows them to setup the test data,
Act – the action that is being tested, and
Assert – check the output of the action for correctness.

The base will also have a method called Run(), which would call the Arrange(), Act() and Assert() in right sequence. This is the actual TestMethod which the “Test framework” will run.


[TestMethod]
public void Run()
{
Arrange();
Act();
Assert();
}

Here is what our base Test-Class would look like,

public abstract class BaseTest
{
[TestMethod]
public void Run()
{
Arrange();
Act();
Assert();
}
protected abstract void Arrange();
protected abstract void Act();
protected abstract void Assert();
}

And a concrete implementation would look something like this,

[TestClass]
public class When_Customer_Buys_an_items : BaseTest
{
private Cart _cart;
private Bill _bill;
private Receipt _receipt;
protected override void Arrange()
{
_cart = new Cart();
_cart.AddItem("Book", 12.49M, ProductType.Exempted, 1);
_cart.AddItem("music CD", 14.99M, ProductType.General, 1);
_cart.AddItem("chocolate", 0.85M, ProductType.Exempted, 1);
}
protected override void Act()
{
_bill = new Bill(_cart);
_receipt = _bill.GenerateReceipt();
}
protected override void Assert()
{
Testing.Assert.AreEqual(_receipt.Items.Count(), 3);
AssertUtil.AssertLineItems(_receipt, _cart);
Testing.Assert.AreEqual(_receipt.SalesTax, 1.50M);
Testing.Assert.AreEqual(_receipt.Total, 29.83M);
}
}

The neat thing about this pattern is the fact that all that the actual test classes need to be concerned about or be responsible for would be arranging the data, acting on this data and asserting the results. The base class will take care of the rest.

Try this out in your project; you would be pleasantly surprised with the structure and discipline this simple pattern brings to the way you write your tests.

Get Your TDD Right with BDD

If you have been using TDD (Test Driven Development) or even maintaining unit-tests at some level, then probably you have already been using these tests as a means to express the business requirement. Writing unit tests before writing the actual implementation code is undoubtedly a very good exercise to brainstorm and understand problem before making an attempt to solve it. But for developers who are writing these tests, there is always a chance of missing out something of business importance or misinterpreting a business requirement. In other words, though these tests might be good for many things but they hardly make any attempt to bridge the age old gap between the business needs and the code development.

Another ailing thing about TDD is the name itself. ‘Test’ driven development is hardly about testing. It has never been about doing the tester’s job. Many people would say TDD is and has always been about good design. I do not completely agree with that either. Good design is a very good by-product of TDD, but that’s not the primary purpose of TDD. The very name and the nomenclature around it, words like Test, Assertion etc, influences the brain (Sapir–Whorf hypothesis) compels it to think that TDD has got something to do with testing.

TDD also requires a fundamental shift in focus as a developer. For decades we developers have been thinking about those grand software designs up-front, creating the databases, the data-access classes, the business facades, re-usable components and what not before addressing the actual business needs in small bits. TDD done right demands for a drastic change in this mindset. It requires the developer to think about the business needs first. Pick a very tiny slice from it which still has a business value and write only as much code that is required to implement this piece of business value. Really, this might not be the most intuitive thing to do for most of the developers. Moreover, this ‘no up-front design’ idea (though I do not completely agree with it) does not work in the best interest of someone like me who sell themselves as ‘Architects’. Pun intended!

No wonder so many teams struggle to get TDD right.

Due to some of these inherent problems with TDD and the huge gray area without sufficient guidelines on what and how much to test, people like Dan North came out with the idea of BDD a.k.a. Behavior Driven Development. The idea behind BDD is pretty simple. The idea is to provide a common tool and guideline to both the business and development folks. This tool/guideline would allow the business analysts and SME’s to write the business requirements (in a specified format) using english-like language. They would write the different business scenarios around the requirement. In other word, they would define the ‘Behavior’ of the system. The same tool would be able to generate or at least guide developers write the test-cases from these scenarios. The developers would implement these test-cases one by one and make all the tests go green. And TADA! In a perfect world, you have a business requirement translated into a working code.

With BDD the point really is to be able to write tests which are more relevant. Tests which matter. Tests which are derived right out of the business requirements and scenarios, coming right out of the horses mouth. BDD is about putting some more structure and discipline into the Test Driven practices you already claim to do.

Code for Testability

Testability is the degree of ease with which a piece of code can be tested. Anything that makes a piece of code harder to test reduces Testability. This gives the bugs an opportunity to hide for longer and conceal themselves better. Hence, it is always advisable to design and code with Testability in mind.

I always used to wonder that why should somebody like me who is more responsible for coding and designing part of the software development process be concerned about Testability? Why should I take the pain to coding for testability? What does it bring to the table for somebody like me?

Well! The answer is, “better design”.

In order to make a code more testable, the classes must have the ability to be tested in isolation. This means more mockable classes. It is easier to mock classes which are interface driven (Yes, we are talking about Design by contract here). Also, in order to make classes easily mockable, they must be as loosely coupled as possible. The dependencies must be abstracted out. Hence, good design practices like Dependency Inversion Principle becomes an obvious candidate for consideration.

To achieve high testability, i.e. degree of ease with which a piece of code can be tested, the classes must be smaller, focused and cohesive in nature. That is, they should focus on one or few related piece of functionality. Such classes are easier to maintain and tend to have neater Separation of Concerns. Such classes are closer to Single Responsibility Principle at their hearts.

The more testable the code is, the more coverage it tends to have. This is because of a simple reason that writing more and more tests for such code is easier. This allows the developers more cushion to refactor those pieces of code which has become obsolete and ugly over a period of time.

Hence, coding for Testability is all about writing more maintainable, flexible and robust code. Code which has a solid foundation and code which can withstand the only constant of software development life – change.

Test Driven Development

As a software developer I always found it very embarrassing to face the truth that my code is also entitled to have its share of bugs. I would find it so very irritating when I have worked passionately for hours to build something and a tester would walk-in with a sheet in hand and suggest “hey bro! You missed out”. I might argue with him but deep down inside I would know the fact that I have indeed “missed out” on something. This is one embarrassment we developers go through all the time but it is only us who could be blamed for this. We don’t think enough before jumping into the code.

There have been so many instances where I have felt like rewriting a piece of code which has become ugly over a period. These code would smell. But my boss would silence my itch with a question that “why would you want to finger something which is up and running?”.

While these problems had become a way of life for me, I came across a refreshing way of software development – Test Driven Development (TDD). TDD is a process pattern for constructing iterations of development projects that employ unit testing. It is a core tenet of “Agile Development”, specifically the Extreme Programming (XP) camp. Unlike the conventional approach to software development where coding is followed by testing, TDD requires to have the tests in place before writing any code. These test-cases serve as the specification and scope for the functionality to be provided.

TDD Life cycle

Add a test: Add a new test. The test must fail for the first time as because the test must be created before any the functionality itself.

Run the test: The test must fail for the first time.

Write code: Write code to develop the functionality.

Run the test again: All the test written for the functionality must pass indicating that all specifications have been met. If not, refactor the code until all the tests pass.

TDD requires developers to think and think hard about specification and scope of the functionality to be developed before writing any code. This ensures that a good deal of brain storming going into understanding the problem before making any efforts to solve it.
Refactoring code is a risky affair because you can always run into this risk of “breaking something while making something”. Having unit-tests in place gives developers the cushion to refactor code confidently. After making the code change the developer can simply run the automated test and to ensure that everything is alright.

Coding for testability demands a much more civilized design to be in place. The code must have “Separation of Concerns” clearly defined. It must be orthogonal. It must follow “Dependency Inversion” Principal. A code not easily testable simply suggests a flaw in the design.

As a newbie, I found writing TDD a little time consuming and cumbersome. But as I get more and more used to it I can really see an increase in my productivity and improvement in the quality of code that I write. The benefit that I reap by investing a little extra time is huge. It’s definitely worth a shot.