Skip to content
On this page

Fixture

In unit testing, we often write multiple @Test methods to group and classify tests for the target code. During testing, we frequently encounter scenarios where an object needs initialization before the test and cleanup afterward. If we write this repetitive code in every @Test method, it becomes cumbersome.

JUnit provides a way to write fixed code for setup before tests and cleanup after tests, known as Fixture.

Let's look at a specific example using the Calculator class:

java
public class Calculator {
    private long n = 0;

    public long add(long x) {
        n = n + x;
        return n;
    }

    public long sub(long x) {
        n = n - x;
        return n;
    }
}

This class is simple, but when testing, we need to initialize the object. Instead of writing initialization code in every test method, we use @BeforeEach for setup and @AfterEach for cleanup:

java
public class CalculatorTest {

    Calculator calculator;

    @BeforeEach
    public void setUp() {
        this.calculator = new Calculator();
    }

    @AfterEach
    public void tearDown() {
        this.calculator = null;
    }

    @Test
    void testAdd() {
        assertEquals(100, this.calculator.add(100));
        assertEquals(150, this.calculator.add(50));
        assertEquals(130, this.calculator.add(-20));
    }

    @Test
    void testSub() {
        assertEquals(-100, this.calculator.sub(100));
        assertEquals(-150, this.calculator.sub(50));
        assertEquals(-130, this.calculator.sub(-20));
    }
}

In the CalculatorTest, there are two methods annotated with @BeforeEach and @AfterEach. These methods are automatically executed before and after each @Test method.

The sequence in which JUnit runs this test code is as follows:

java
for (Method testMethod : findTestMethods(CalculatorTest.class)) {
    var test = new CalculatorTest(); // Create Test instance
    invokeBeforeEach(test);
        invokeTestMethod(test, testMethod);
    invokeAfterEach(test);
}

As you can see, @BeforeEach and @AfterEach "wrap" around each @Test method.

@BeforeAll and @AfterAll

Some resources, such as initializing a database, may be more complex and time-consuming. JUnit also provides @BeforeAll and @AfterAll, which run before and after all @Test methods. The execution order is:

java
invokeBeforeAll(CalculatorTest.class);
for (Method testMethod : findTestMethods(CalculatorTest.class)) {
    var test = new CalculatorTest(); // Create Test instance
    invokeBeforeEach(test);
        invokeTestMethod(test, testMethod);
    invokeAfterEach(test);
}
invokeAfterAll(CalculatorTest.class);

Since @BeforeAll and @AfterAll only run once before and after all test methods, they can only initialize static variables, for example:

java
public class DatabaseTest {
    static Database db;

    @BeforeAll
    public static void initDatabase() {
        db = createDb(...);
    }

    @AfterAll
    public static void dropDatabase() {
        ...
    }
}

In fact, @BeforeAll and @AfterAll must also be annotated on static methods.

Best Practices for Writing Fixtures

  • For instance variables, initialize them in @BeforeEach and clean them up in @AfterEach. Each test method has a separate instance, so they don't interfere with each other.
  • For static variables, initialize them in @BeforeAll and clean them up in @AfterAll. These variables are shared among all @Test methods.

Most of the time, @BeforeEach and @AfterEach are sufficient. You only need @BeforeAll and @AfterAll when resource initialization takes too long, and reuse is necessary.

Finally, note that JUnit creates a new instance of XxxTest before running each @Test method. This means that member variables inside each @Test method are independent, and the state of a member variable cannot be carried over from one test method to another.

Summary

Writing a Fixture involves creating a @BeforeEach method to initialize test resources and a @AfterEach method to clean them up for each @Test method.

If necessary, you can also write @BeforeAll and @AfterAll methods to initialize time-consuming resources with static variables, which are executed only once before and after all @Test methods.

Fixture has loaded