The Arrange, Act, and Assert (AAA) Pattern: A Functional Approach

Lover of lying on the floor and listening to music at high volumes, craft beer enthusiast, gym rat. Also automation engineer/backend developer at Mews.

For those just starting their foray into the world of automated testing, things can rapidly get out of control. It’s like a never-ending pit of acronyms, abbreviations, and long, complicated, “science fiction-y” words. And a google search – that dependable beacon to the lost and confused – often returns results contrary to what you were hoping to find.

Take AAA for example. A simple google search for “AAA” would bring you about as far away from automated testing as you would care to go. The most popular results ranging from auto insurance companies to J-pop bands.

J-pop band KKK
J-pop band KKK

Now I neither want to comment on auto insurance, nor do I wish to critique the J-pop genre. Rather, in the next few paragraphs, I hope to shed a new light on one of the standard testing patterns used in the testing industry, the Arrange, Act, and Assert (AAA) pattern.

What is it? 🤷‍♂️ ️

The AAA pattern is a pattern for structuring tests. It breaks each test down into three parts – Arrange, Act, and Assert – where each part is a step leading to the next. The arrange step sets up the test’s input values. The act step prompts the primary function being tested. And finally, the assert step verifies that the output of the function is what was expected.

In other words, each step in the pattern is a function whose return value is the input of the following step. For example,

assert(act(arrange()));

Here each step is represented as a function called in order from right to left. Arrange is called first. The result of arrange is passed to the act function, and act’s result is subsequently passed to assert. Assert returns void, a bool, or whatever you as the author determine valuable for the purpose of the test.

Setup and usage 👩‍💻

To set this up, you must first create a separate test method accessible to every test. One solution is to create a ‘TestFixture’ class that is inherited by every test class. This class could look something like this –

public class TestFixture{    
    public void Test<TArrangeResult, TActResult>(
        Func<TArrangeResult> arrange,
        Func<TArrangeResult, TActResult> act,
        Action<TActResult> assert)
    {       
        assert(act(arrange()));    
    }
}

In this C# class example, there is a single method called ‘Test’ with three parameters that must all be functions. Each function has a generic signature. The signatures are generic here so the method can be more widely applied throughout your test cases.

The test method can then be called inside a test. With this in place, your job becomes creating the functions to be passed as parameters. One solution is to use anonymous functions.

[Test]
public void CanSellItem()
{    
    Test(        
        arrange: () => new Item(),        
        act: item => Sell(item),        
        assert: actResult => Assert.IsTrue(actResult)    
    );
}

Using the lambda syntax in C#, the latter example creates anonymous functions for the arrange, act, and assert parameters. These anonymous functions are passed and called inside the test method in the order you set them up.

The test method can then be used in several different test cases. That is, only if the signatures of the functions you create match the signatures of the test method’s parameters.

Take the arrange parameter in the test method shown in Figure 1 for example. It uses ‘Func<TArrangeResult>’ as its signature. Without going into detail on C# delegates, this signature is basically saying “I accept any function that takes no parameters and returns a single object”. So, as long as you create a function matching the signature’s description, it should fit in with your test method.

The signatures of the test method’s parameters can even be changed to accept more complex and interesting functions. Do you want an arrange function that takes several parameters? No problem! You can even create different test methods to handle different groups of test cases.

New(ish) way of thinking 👨‍🎓

All in all, the AAA pattern is only meant to be a guideline. It is a structure, or a way of thinking about and arranging your tests so that they can be clearly understood. There is no package or downloadable tool with documentation. Instead, it defers to you, the author, how best to create and use it.

I have found that creating functions for each step in the pattern offers one of the most versatile solutions. Functions are like building blocks; you can reuse blocks in different designs. Or, in our case, you can reuse different functions in different test cases.

And while functions aren’t a new concept, they do offer a breath of fresh air for the AAA pattern.

Developer looking at diagrams on a whiteboard

Try it out 🤹‍♀️

What I’ve shown you here is a basic design for creating functions that represent each step in the AAA pattern and collecting those functions in a single test method. It is meant to act as a starting point for you to build more complex and interesting test methods. In the spirit of the AAA pattern, I defer to you to determine how best to build it for your case.

Of course, if you get stuck implementing this on your own, have questions, or just want to say hi, always feel free to reach out. I’ll be happy to help.


Also, you can watch me talk about AAA pattern here:

QA meetup: Trends and challenges
Lover of lying on the floor and listening to music at high volumes, craft beer enthusiast, gym rat. Also automation engineer/backend developer at Mews.
Share:

More About