JUnit 5 @BeforeAll: One-Time Setup for Your Tests

When writing automated tests, we often need to perform setup tasks before the tests run. This could involve initializing a database connection, starting a web server, or preparing a complex object that’s expensive to create. JUnit 5 provides a powerful set of annotations to manage the lifecycle of your tests, and for one-time setup logic, the @BeforeAll annotation is the perfect tool.

In this guide, we’ll take a deep dive into the @BeforeAll annotation, understand its rules, and explore its usage with practical code examples. We’ll also clarify the key difference between @BeforeAll and @BeforeEach.

@BeforeAll is the direct replacement for JUnit 4’s @BeforeClass annotation.

What is @BeforeAll?

The @BeforeAll annotation is used to signal that a method should be executed only once, before any tests in the current test class are run. It’s ideal for setup operations that are shared across all test methods in a class but are too resource-intensive to run before each and every test.

Core Rules for @BeforeAll Methods

To use @BeforeAll correctly, you need to follow a few simple rules:

  • The annotated method must be a static method by default.
  • It must not return any value (i.e., its return type should be void).
  • It cannot be a private method. It can be publicprotected, or package-private.

The reason the method must be static is tied to JUnit 5’s default test instance lifecycle. By default, JUnit creates a new instance of the test class for every single test method. Since the @BeforeAll method runs before any of these instances are created, it must be associated with the class itself (as a static method) rather than a specific instance.

Example 1: Standard Usage with a Static Method

Let’s look at the most common way to use @BeforeAll. In this example, we’ll also use its counterpart, @AfterAll, which runs once after all tests in the class have completed. This is perfect for cleanup tasks like closing database connections or shutting down servers.

The Code

Here we have a simple test class with two test methods. The setupAll() method will run once before both tests, and teardownAll() will run once after both are finished.

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class StandardBeforeAllExampleTest {

    @BeforeAll
    static void setupAll() {
        System.out.println("==> Executing setupAll()... (runs once)");
    }

    @Test
    void testMethodOne() {
        System.out.println("--> Executing testMethodOne()");
    }

    @Test
    void testMethodTwo() {
        System.out.println("--> Executing testMethodTwo()");
    }
    
    @AfterAll
    static void teardownAll() {
        System.out.println("==> Executing teardownAll()... (runs once)");
    }
}

Execution Output

When you run this test class, you’ll see the following output, clearly showing the execution order:

==> Executing setupAll()... (runs once)
--> Executing testMethodOne()
--> Executing testMethodTwo()
==> Executing teardownAll()... (runs once)

As you can see, the setup and teardown methods were each executed only once, bookending the execution of the individual test methods.

Example 2: The Non-Static Alternative with @TestInstance

What if you don’t want to use static methods? Perhaps you want to initialize non-static instance fields in your setup method and use them across your tests. JUnit 5 provides a flexible solution for this: changing the test instance lifecycle.

By annotating your test class with @TestInstance(TestInstance.Lifecycle.PER_CLASS), you tell JUnit to create only a single instance of the test class and reuse it for all the test methods. Because there’s a single instance available from the beginning, your @BeforeAll and @AfterAll methods no longer need to be static.

The Code

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;

// Change the test instance lifecycle to PER_CLASS
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class PerClassLifecycleExampleTest {
    
    // This method can now be non-static
    @BeforeAll
    void setupAll() {
        System.out.println("==> Executing setupAll() on a single instance...");
    }

    @Test
    void testMethodOne() {
        System.out.println("--> Executing testMethodOne()");
    }

    @Test
    void testMethodTwo() {
        System.out.println("--> Executing testMethodTwo()");
    }
    
    // This method can also be non-static
    @AfterAll
    void teardownAll() {
        System.out.println("==> Executing teardownAll() on a single instance...");
    }
}

Execution Output

The execution output is functionally identical, proving the lifecycle works as expected:

==> Executing setupAll() on a single instance...
--> Executing testMethodOne()
--> Executing testMethodTwo()
==> Executing teardownAll() on a single instance...

@BeforeAll vs. @BeforeEach

A common point of confusion for newcomers is the difference between @BeforeAll and @BeforeEach. The distinction is simple but critical for writing efficient tests.

Feature@BeforeAll@BeforeEach
Execution ScopeRuns once per test class, before any tests start.Runs before each and every @Test method in the class.
Method SignatureMust be static (unless @TestInstance(Lifecycle.PER_CLASS) is used).Is typically a non-static instance method.
Common Use CaseHeavy, one-time setup: database connections, starting servers, initializing shared fixtures.Lightweight setup or state reset: creating new mock objects, resetting variables to a default state before each test.

Conclusion

The @BeforeAll annotation is an essential part of the JUnit 5 toolkit, enabling you to manage expensive setup operations efficiently. By understanding its core behavior and the flexibility offered by @TestInstance(Lifecycle.PER_CLASS), you can write cleaner, faster, and more maintainable tests.

Remember to use @BeforeAll for tasks that truly need to run only once per class and use @BeforeEach for resetting state between individual tests. Choosing the right tool for the job will significantly improve the quality of your test suite.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.