Chuniversiteit.nl
“Heap, Heap, Array!”

Structure your tests using the Arrange-Act-Assert pattern

Arrange-Act-Assert is a standardised approach to writing tests that makes them easier to read and helps you avoid some bad practices.

Illustration of trias politica with three branches of the United States (the US Capitol, the White House, and the Supreme Court). “Arrange” represents the legislative branch, “Act” the executive branch, while “Assert” is the judicial branch.
Arrange-Act-Assert is like a trias politica for test cases

Although it’s possible to apply all kinds of metrics and mathematical principles to software testing, in practice most experienced developers just follow their intuition when they write tests and don’t really think that hard about how they write them. As it happens, writing good test cases is often more of an art than a science.

Fortunately there’s that can help you write test cases that are easier to understand and debug: the Arrange-Act-Assert pattern.

The pattern

Link

The Arrange-Act-Assert pattern is based on the idea that many tests do a combination of the following three things:

  1. Arrange: Set up everything that we need before we can start our test, e.g. by creating, initialising or modifying test objects.

  2. Act: Perform some action on the system under test (SUT), i.e. the thing that is being tested. The action is usually a function or method call.

  3. Assert: Verify that the action was handled properly by the SUT, for instance by checking if a function call produced the expected result.

For example, a simple unit test in Python might look a bit like this:

This test is only three lines long, so it’s easy to see what’s happening here: first, we create a new Student object with a date of birth, then we use its age_on_date method to calculate the student’s current age in years on a specific date.

However, most tests that you encounter in real-life codebases consist of more than just three lines. Here’s an example of a slightly longer test:

Not only is this test longer than the test that we saw earlier, it also resembles a wall of text, which makes it hard to scan!

And this test is actually not even that long; some tests require dozens of lines of code just to set up things like mocks and fixtures, or to verify that the SUT produced the expected result.

Paragraphs for your code

Link

Longer texts usually consist of multiple paragraphs, which are separated from each other using newlines. We can do the same thing in our code.

That same test already looks a bit more readable and a little less threatening when we add some newlines to it:

The first section (or “paragraph”) is responsible for preparing the student object. The second section calculates and sets the student’s GPA, while the third section contains our assertions.

We can restructure this code a bit so that each of the three sections in the test corresponds with “Arrange”, “Act”, and “Assert” respectively:

You can now instantly see that this test covers the is_happy_with_gpa() method without having to read the entire test. We had to make some small sacrifices though:

  • All setup code is now grouped together, even though it consists of two distinct parts.

  • In previous versions the is_happy_with_gpa() was called as part of the assertion. But because we separated the action and assertions here, we had to introduce an intermediate variable result that stores the result from our action until we’re ready to check its value.

As a final touch, we can use single-line comments to add headings to each of the three major sections. This also allows us to split the “Arrange” section into two paragraphs again, as in the previous snippet:

There are still , but at least we’ve managed to make things a little bit better!

By the way…

Link

You should know that this was an incredibly opinionated article. Not everyone likes the Arrange-Act-Assert pattern. People don’t always use it. Even I don’t always use it.

In this section I’ll try to explain when and how I use it.

Headings

Link

When I write tests I usually create a little template that looks a bit like the following snippet:

The > has no special meaning. I simply use it to differentiate these section headings from regular single-line comments:

Some people use capital letters (# ARRANGE), other symbols (## Arrange), or omit the whitespace between the comment symbol and the heading (#> Arrange, #arrange).

Speaking of whitespace: you can also omit the newlines between sections, if you want your tests to look small.

You can also use other section titles (e.g. “Given”/“When”/“Then”) if you want. After all, it’s your code!

Omitting headings

Link

Note that you don’t have to include all three sections:

  • Many tests are so simple that they don’t require any setup;
  • Tests that verify if the SUT throws an exception rarely include assertions within the function body;
  • Sometimes the “Act” and “Assert” sections are so trivial that there’s no need to separate them.

If your test is simple enough, you can even leave them out entirely:

It’s also okay to omit headings if you feel that a test is just as easy to understand without them, as we saw in the first snippet or in this modified version that separates the three sections using only newlines:

It’s not just for Python

Link

All of the examples you’ve seen up to this point are written in Python, but the Arrange-Act-Assert pattern works for virtually any language. Here are two examples that use Java and PHP:

Arrange-Act-Assert-Act-Assert

Link

You might run into a situation where you want to include two distinct sets of assertions, e.g. when you want to verify that an object can be successfully created and updated within the same test.

. It’s called Arrange-Act-Assert for a reason.

What to assert when you’re expecting

Link

The Arrange-Act-Assert pattern doesn’t work very well for tests that make use of mocks. Such tests often don’t have a section for traditional assertions. Instead, the assertions are written in the form of expectations that are defined during the “Arrange” step.

For such tests you could use a template that includes two “Arrange” sections; one for “normal” variables and objects, and one for mocks: