How to simulate real BeforeAll and AfterAll in JUnit 5
Tags: unittest, junit, java, testing
Published: 2024-07-05 08:51:00

## Introduction

JUnit 5 is a well-known Java Testing Framework/Library across developers. It’s the evolution of JUnit 4 and carries with it a lot of awesome features. One of the most important ones is setting pre and post-conditions as knowing by the terms Before (pre-condition) and After (post-condition).

It has 2 supported ways: Before/After All and Before/After Each.
The “All” part means that a code block can be executed as pre or post-condition before or after it can initialize all tests. The “Each” part means that a code block can be executed as a pre or post-condition before or after each test.

The [JUnit 5 official docs](https://junit.org/junit5/docs/snapshot/user-guide/#overview) say the following about these strategies, which are annotations:

| Annotation | Description |
|--|--|
| `@BeforeEach` | Denotes that the annotated method should be executed **before each** `@Test`, `@RepeatedTest`, `@ParameterizedTest`, or `@TestFactory` method in the current class; analogous to JUnit 4’s `@Before`. Such methods are inherited unless they are overridden |
| @AfterEach | Denotes that the annotated method should be executed **after each** `@Test`, `@RepeatedTest`, `@ParameterizedTest`, or `@TestFactory` method in the current class; analogous to JUnit 4’s @After. Such methods are inherited unless they are overridden. |
| `@BeforeAll` | Denotes that the annotated method should be executed **before all** `@Test`, `@RepeatedTest`, `@ParameterizedTest`, and `@TestFactory` methods in the current class; analogous to JUnit 4’s `@BeforeClass`. Such methods are inherited unless they are overridden and must be static unless the “per-class” test instance lifecycle is used. |
| `@AfterAll` | Denotes that the annotated method should be executed **after all** `@Test`, `@RepeatedTest`, `@ParameterizedTest`, and `@TestFactory` methods in the current class; analogous to JUnit 4’s `@AfterClass`. Such methods are inherited unless they are overridden and must be static unless the “per-class” test instance lifecycle is used. |

## What problem we are trying to solve

Only by looking at the annotations, we understand that it covers most of the scenarios we want for our tests.
We have the `@BeforeAll` and `@AfterAll` annotations as a general pre or post-condition.

One common testing pattern applied in good testing architectures is the BaseTest class: a place where we can add pre and post-conditions that will shared across different tests by inheritance. Normally, we want to control these conditions in different ways. Developers have different ways to control it through the BaseTest pattern:

* open the browser only once when any test starts to save time
* keep a container (Testcontainer) opened for different test classes
* ingest data before any test is executed and remove it after all are executed

I have bad news: JUnit 5 doesn’t have a way to control the three mentioned scenarios as the `@BeforeAll` and `@AfterAll` are executed per test instance, meaning per test class. Other frameworks like TestNG [have this ability](https://testng.org/#_annotations) called `@BeforeSuite` and `@AfterSuite`, and it’s exactly what we want that JUnit 5 does not support.

Let’s understand how we could use JUnit 5 for this and how to fix this problem.

## The BeforeAllCallback and AfterAllCallback interfaces

You might do some Google search, like I did infinite times, encountering the [BeforeAllCallback](https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/extension/BeforeAllCallback.html) and [AfterAllCallback](https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/extension/AfterAllCallback.html) interfaces which are [Extensions](https://junit.org/junit5/docs/snapshot/user-guide/#extensions-registration) of [Testing Lifecycle Callbacks](https://junit.org/junit5/docs/snapshot/user-guide/#extensions-lifecycle-callbacks). It seems a good solution as these interfaces enable you to run the `@BeforeAll` or `@AfterAll`.

```java
public class MyExtension implements BeforeAllCallback, AfterAllCallback {
    @Override
    public void afterAll(ExtensionContext context) {
        // pre general condition
    }
 
    @Override
    public void beforeAll(ExtensionContext context) {
        // post general conditions
    }
}
```

![UML diagram showing the current way of using the pre and postcondition annotations](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tv2qmqqvp825z6xlju23.png)

The UML diagram shows a BaseTest using a JUnit 5 Extension called `MyExtension` that implements the `BeforeAllCallback` and `AfterAllCallback`. The base test is used in the `FeatureTest1` and `FeatureTest2`. Note that the `MyExtension` has the `beforeAll` and `afterAllMethods`, as well as the `BaseTest` has it.

It solves the problem as the `MyExtension` would serve as the global before and after, as the ones in the `BaseTest` would run per test instance, meaning running when `Feature1Test` and `Feature2Test` run. Unfortunately, it’s not the case. If we would add only a `System.out.println()` call in each method, the output would be the following:

```
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.eliasnogueira.feature1.Feature1Test
MyExtension.beforeAll
BaseTest.beforeAll
Feature1Test.test1
Feature1Test.test2
Feature1Test.test3
BaseTest.afterAll
MyExtension.afterAll
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.018 s -- in com.eliasnogueira.feature1.Feature1Test
 
[INFO] Running com.eliasnogueira.feature2.Feature2Test
MyExtension.beforeAll
BaseTest.beforeAll
Feature2Test.test1
Feature1Test.test2
BaseTest.afterAll
MyExtension.afterAll
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.002 s -- in com.eliasnogueira.feature2.Feature2Test
```

You can see that the methods in the `MyExtension` run for every test class, as well as the `BaseTest`. This means that JUnit runs it per test instance. Unfortunately, JUnit 5 doesn’t have a solution for a general precondition before or after any test for the whole test execution.

> Want to see it in action?
> – Clone this repo: git clone https://github.com/eliasnogueira/junit5-before-after-all
> – Switch to the no-solution branch
> – Run mvn test

## How to solve it

There is a light at the end of the tunnel and it’s not difficult. Like everyone, I Google it and found an interesting workaround on this Stackoverflow thread: https://stackoverflow.com/questions/43282798/in-junit-5-how-to-run-code-before-all-test.

**_Be aware that this is a workaround and might not work in future JUnit versions._**

The solution is based on using the `BeforeAllCallback` interface, with a thread lock in case parallel tests run to solve the general precondition and the JUnit storage mechanism to mimic the postcondition using the [ExtensionContext.Store.CloseableResource](https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExtensionContext.Store.CloseableResource.html) interface. Don’t worry, I will break down the implementation.

### The example

It is a simple one just to show you that the approach works. The example shows a general BaseTest and a BaseTest per feature, where an extension will be created to give the ability of execution a general pre and postcondition.

### The extension implementation

The implementation can be done in four steps, and the final solution will look like this:


![UML diagram showing the proposal implementation of the before and after to solve the current problem](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2mzuodqfxn5lvc24e3tp.png)

```java
1  public class SetupExtension implements BeforeAllCallback, Store.CloseableResource {
2 
3     private static final Lock LOCK = new ReentrantLock();
4     private static volatile boolean started = false;
5 
6     @Override
7     public void beforeAll(ExtensionContext context) {
8         LOCK.lock();
9  
10         try {
11            if (!started) {
12                started = true;
13                System.out.println("[pre-condition] Should run only once");
14                context.getRoot().getStore(Namespace.GLOBAL).put("Placeholder for post condition", this);
15            }
16        } finally {
17            // free the access
18            LOCK.unlock();
19        }
20    }
21 
22 
23    @Override
24    public void close() {
25        System.out.println("[post-condition] Should run only once");
26    }
27 }
```

**Implementation of the necessary interfaces**

Line 1 shows that the two interfaces: `BeforeAllCallback` overriding the `beforeAll()` method which will control the general precondition and `ExtensionContext.Store.CloseableResource` overrides the `close()` method which will mimic the general postcondition.

**Controlling the single execution of the beforeAll**

To guarantee that it will be executed only once one strategy must be applied: control that it has started, so the `beforeAll()` won't execute again.

Line 8 shows that we are locking the thread. This is necessary to ensure that any parallel execution will be possible. Line 11 checks if the code had any previous execution. When it's the first time the `boolean started` is set as `true` ensure it won't go to the code block in the subsequent runs. The `finally` section unlocks the thread.

**Implementing the general precondition**

Any necessary implementation for the general precondition should be placed inside the `if` condition, simple like that. We can see this in the line 13.

**Add a signal (storage) to mimic the general postcondition**

The way to mimic the general postcondition here is through the [Store](https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExtensionContext.Store.html). In JUnit 5, we can store objects for later retrieval in an extension and it can be done using the `getStore(context).put(key, value)` where the `context` is the root or current context and the `key,value` are the key and value to add to its storage.

Line 14 creates a dummy store object for later usage in the automatic `close()` method invocation.

**Implement the general postcondition**

The `close()` method from the `ExtensionContext.Store.CloseableResource` interface will be invoked when the extension lifecycle ends [[reference](https://junit.org/junit5/docs/current/user-guide/#extensions-keeping-state)]. This is the last opportunity to execute any code before the program exits. In this way, we can simulate the general postcondition.

## Code example

The https://github.com/eliasnogueira/junit5-before-after-all project shows the implementation based on this article’s explanation matching the diagram in “The example” section.

While running the tests you will see the following output:

```
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.eliasnogueira.feature1.Feature1Test
[pre-condition] Should run only once
BaseTest.beforeAll
BaseFeature1Test.beforeAll
Feature1Test.test1
Feature1Test.test2
Feature1Test.test3
BaseFeature1Test.afterAll
BaseTest.afterAll
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.019 s -- in com.eliasnogueira.feature1.Feature1Test
 
[INFO] Running com.eliasnogueira.feature2.Feature2Test
BaseTest.beforeAll
BaseFeature2Test.beforeAll
Feature2Test.test1
Feature1Test.test2
BaseFeature2Test.afterAll
BaseTest.afterAll
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.002 s -- in com.eliasnogueira.feature2.Feature2Test
[post-condition] Should run only once
```

Note that the general precondition is the text output as `[pre-condition] Should run only once` and the general postcondition is the output as `[post-condition] Should run only once`. You can see them at the beginning and the end of all test executions, respectively.

Using this simple approach you can have the general pre and postcondition in your code.
Happy tests!