Test lifecycle

As we saw in Chapter 1Retrospective on software quality and Java testing, a unit test case is composed of four stages:

  1. Setup (optional): First, the test initializes the test fixture (before the picture of the SUT).
  2. Exercise: Second, the test interacts with the SUT, getting some outcome from it as a result.
  3. Verify: Third, the outcome from the system under test is compared to the expected value using one or several assertions (also known as predicates). As a result, a test verdict is created.
  4. Teardown (optional): Finally, the test releases the test fixture to put the SUT back into the initial state.

In JUnit 4, there were different annotations to control these test phases. JUnit 5 follows the same approach, that is, Java annotations are used to identify different methods within Java classes, implementing the test life cycle. In Jupiter, all these annotations are contained in the package org.junit.jupiter.api.

The most basic JUnit annotation is @Test, which identifies the methods that have to be executed as tests. Therefore, a Java method annotated with org.junit.jupiter.api.Test will be treated as a test. The difference of this annotation with respect to JUnit 4's @Test is two folded. On the one hand, the Jupiter @Test annotation does not declare any attributes. In JUnit 4,  @Test can declare the test timeout (as long attribute with the timeout in milliseconds), on the other hand, in JUnit 5, neither test classes nor test methods need to be public (this was a requirement in JUnit 4).

Take a look at the following Java class. Possibly, it is the simplest test case we can create with Jupiter. It has simply a method with the @Test annotation. The test logic (that is the exercise and verify stages as described before) would be contained inside the method myTest.

package io.github.bonigarcia;

import org.junit.jupiter.api.Test;

class SimpleJUnit5Test {

@Test
void mySimpleTest() {
// My test logic here
}

}

The Jupiter annotations (also located in the package org.junit.jupiter.api) aimed to control the setup and tear down stages in JUnit 5 tests are described in the following table:

JUnit 5 annotation Description JUnit 4's equivalence
@BeforeEach Method executed before each @Test in the current class @Before
@AfterEach Method executed after each @Test in the current class @After
@BeforeAll Method executed before all @Test in the current class @BeforeClass
@AfterAll Method executed after all @Test in the current class @AfterClass
Methods annotated with these annotations (@BeforeEach, @AfterEach, @AfterAll, and @BeforeAll) are always inherited. 

The following picture depicts the order of execution of these annotations in a Java class:

Jupiter annotations to control the test lyfecycle

Let's go back to the generic structure for tests we saw at the beginning of this section. Now, we are able to map the Jupiter annotations to control the test lifecycle with the different parts of a test case. As illustrated in the following picture, we carry out the setup stage by annotating methods with @BeforeAll and @BeforeEach. Then, we carry out the exercise and verify stages in methods annotated with @Test. Finally, we carry out the tear down process in the methods with @AfterEach and @AfterAll.

Relationship among the unit test cases stages and the Jupiter annotations

Let's see a simple example, which uses all these annotations in a single Java class. This example defines two tests (that is, two methods annotated with @Test), and we define additional methods for the rest of the test life cycle with the annotations @BeforeAll, @BeforeEach, @AfterEach, and @AfterAll:

package io.github.bonigarcia;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class LifecycleJUnit5Test {

@BeforeAll
static void setupAll() {
System.out.println("Setup ALL TESTS in the class");
}

@BeforeEach
void setup() {
System.out.println("Setup EACH TEST in the class");
}

@Test
void testOne() {
System.out.println("TEST 1");
}

@Test
void testTwo() {
System.out.println("TEST 2");
}

@AfterEach
void teardown() {
System.out.println("Teardown EACH TEST in the class");
}

@AfterAll
static void teardownAll() {
System.out.println("Teardown ALL TESTS in the class");
}

}

If we run this test class, first @BeforeAll will be executed. Then, the two test methods will be executed sequentially, that is, the first one and then the other. In each execution, the setup method annotated with @BeforeEach will be executed before the test, and then the @AfterEach method. The following screenshot shows an execution of the tests using Maven and the command line:

Execution of a Jupiter test which controls its lifecycle
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.75.165