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.

Tale of a Pragmatic Team

Take the world as it is, not as it ought to be
                                                                                                  — German Proverb

With the passing every single year in the world of software development I find myself more and more convinced of the fact that every project and every team is unique. They have unique situations and requirements. One should accept and adopt tools and processes only to the extent that fits the personality and culture of the project team and caters to the needs of the project. Let me share a tale of a team that I worked with in recent times.

The team was maintaining a suit of unit-tests around the code. Though the code coverage was decent, the execution coverage was really poor. If you ran the test-suit, at any given point, many if not most tests would fail. Not a happy situation to be in, especially, if you are willing to leverage the benefits of units tests. One of the most important benefits of having a unit tests around your code is to provide you with the courage and the confidence to refactor your code fearlessly. You should be able to make a change in the code and run the tests to make sure everything is alright. And this is what the team was badly missing with it’s tests. Any “red” test that we came across would not clearly conclude that weather the test failed because of bad data or the functionality was actually broken. We dug into the each and every unit-test. To our relief we found almost all the failing tests were failing because bad test-data and not broken logic. Believe me or not this is the case with many teams claiming to maintain unit-tests.

We fixed the test-data, created dummies, mocks and stubs where required and made the test-suite hundred percent pass. We would setup data before running the tests and undo the changes to the database (or other states) after the tests ran. Hence, the test-suit always remained automated.

The next logical question then was how are we going to make sure that the tests we fixed this time around would not run into the same issue in the future. The answer was obvious – Continuous Integration (CI). With Microsoft’s Team Foundation Server (TFS), it was fairly easy to set up the build for Continuous Integration. Once we configured the CI, we realized that the builds were heavy and slow as many of these tests talked to the database. It was turning out to be an overkill for the return we were achieving. Again, we followed the “horses for courses” strategy. The pragmatic solution that we all agreed upon was that instead of queuing a build at every check-in, we should trigger the test-run only in the nightly builds. That way we were sure that code integrates at least once every day. Something we happily lived with.

The idea behind telling the story is that tools, process and best-practices are not meant to be followed like commandments. One should certainly look out for and be aware of what’s working for most teams out there, borrow the idea as and when necessary but should not impose them on the team. They should be adopted and accepted according to the teams comfort and adopted as per the teams need.

As you would have already understood that what we followed was not even close to be called TDD. We just maintained unit tests. But at the end of the day we delivered, and delivered well. Isn’t that all that matters? Isn’t it more important to do what works than what is right? After all, isn’t it true that there is no single key for every lock?

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.