Reputation: 393
The following sample test generates the output as printed below.
Sample test:
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
public class OuterTest extends ParentTest implements TestInterface1, TestInterface2
{
@BeforeEach
void outerSetup()
{
System.out.println( " outerSetup" );
}
@AfterEach
void outerTearDown()
{
System.out.println( " outerTearDown" );
}
@Test
void outerTest1()
{
System.out.println( " outerTest1" );
}
@Test
void outerTest2()
{
System.out.println( " outerTest2" );
}
@Nested
class InnerTest
{
@BeforeEach
void innerSetup()
{
System.out.println( " innerSetup" );
}
@AfterEach
void innerTearDown()
{
System.out.println( " innerTearDown" );
}
@Test
void innerTest1()
{
System.out.println( " innerTest1" );
}
@Test
void innerTest2()
{
System.out.println( " innerTest2" );
}
@RepeatedTest(3)
void innerRepeatedTest()
{
System.out.println( " innerRepeatedTest" );
}
@ParameterizedTest
@ValueSource(strings = { "foo", "bar", "baz" })
void innerParameterizedTest( final String input )
{
System.out.println( " innerParameterizedTest - " + input );
}
}
}
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
public class ParentTest
{
@BeforeEach
void parentSetup()
{
System.out.println( "parentSetup" );
}
@AfterEach
void parentTearDown()
{
System.out.println( "parentTearDown" );
}
}
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
public interface TestInterface1
{
@BeforeEach
default void interface1Setup()
{
System.out.println( "interface1Setup" );
}
@AfterEach
default void interface1TearDown()
{
System.out.println( "interface1TearDown" );
}
}
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
public interface TestInterface2
{
@BeforeEach
default void interface2Setup()
{
System.out.println( "interface2Setup" );
}
@AfterEach
default void interface2TearDown()
{
System.out.println( "interface2TearDown" );
}
}
Output:
parentSetup
interface1Setup
interface2Setup
outerSetup
outerTest1
outerTearDown
interface1TearDown
interface2TearDown
parentTearDown
parentSetup
interface1Setup
interface2Setup
outerSetup
outerTest2
outerTearDown
interface1TearDown
interface2TearDown
parentTearDown
parentSetup
interface1Setup
interface2Setup
outerSetup
innerSetup
innerRepeatedTest
innerTearDown
outerTearDown
interface1TearDown
interface2TearDown
parentTearDown
parentSetup
interface1Setup
interface2Setup
outerSetup
innerSetup
innerRepeatedTest
innerTearDown
outerTearDown
interface1TearDown
interface2TearDown
parentTearDown
parentSetup
interface1Setup
interface2Setup
outerSetup
innerSetup
innerRepeatedTest
innerTearDown
outerTearDown
interface1TearDown
interface2TearDown
parentTearDown
parentSetup
interface1Setup
interface2Setup
outerSetup
innerSetup
innerParameterizedTest - foo
innerTearDown
outerTearDown
interface1TearDown
interface2TearDown
parentTearDown
parentSetup
interface1Setup
interface2Setup
outerSetup
innerSetup
innerParameterizedTest - bar
innerTearDown
outerTearDown
interface1TearDown
interface2TearDown
parentTearDown
parentSetup
interface1Setup
interface2Setup
outerSetup
innerSetup
innerParameterizedTest - baz
innerTearDown
outerTearDown
interface1TearDown
interface2TearDown
parentTearDown
parentSetup
interface1Setup
interface2Setup
outerSetup
innerSetup
innerTest1
innerTearDown
outerTearDown
interface1TearDown
interface2TearDown
parentTearDown
parentSetup
interface1Setup
interface2Setup
outerSetup
innerSetup
innerTest2
innerTearDown
outerTearDown
interface1TearDown
interface2TearDown
parentTearDown
For our use case we want to achieve, that the @BeforeEach
/@AfterEach
methods are only called before/after each test in class OuterTest
and before the first resp. after the last test in class InnerTest
, but not between the tests of the inner class.
So the desired output is the following:
parentSetup
interface1Setup
interface2Setup
outerSetup
outerTest1
outerTearDown
interface1TearDown
interface2TearDown
parentTearDown
parentSetup
interface1Setup
interface2Setup
outerSetup
outerTest2
outerTearDown
interface1TearDown
interface2TearDown
parentTearDown
parentSetup
interface1Setup
interface2Setup
outerSetup
innerSetup
innerRepeatedTest
innerTearDown
innerSetup
innerRepeatedTest
innerTearDown
innerSetup
innerRepeatedTest
innerTearDown
innerSetup
innerParameterizedTest - foo
innerTearDown
innerSetup
innerParameterizedTest - bar
innerTearDown
innerSetup
innerParameterizedTest - baz
innerTearDown
innerSetup
innerTest1
innerTearDown
innerSetup
innerTest2
innerTearDown
outerTearDown
interface1TearDown
interface2TearDown
parentTearDown
I tried to change the behavior of the @BeforeEach
/@AfterEach
methods by implementing an extension, which implements an InvocationInterceptor
, but I failed to find out if a test class is the last test in the inner class, which would enable to decide, if the @BeforeEach
/@AfterEach
methods of the outer class should be called or not.
Does anyone know how to achieve this?
Thanks in advance!
Upvotes: 1
Views: 330
Reputation: 9393
If you are able to extract your actual outer setup and teardown methods, you can introduce a second @Nested
class for the outer tests and use @BeforeAll
and @AfterAll
to invoke the outer setup/teardown once:
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.Nested;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
class OuterTest {
void actualOuterSetUp() {
System.out.println("outerSetUp");
}
void actualOuterTearDown() {
System.out.println("outerTearDown");
}
@Nested
class InnerTestA {
@BeforeEach
void outerSetUp() {
actualOuterSetUp();
}
@AfterEach
void outerTearDown() {
actualOuterTearDown();
}
@Test
void outerTest1() {
System.out.println(" outerTest1");
}
@Test
void outerTest2() {
System.out.println(" outerTest2");
}
}
@Nested
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class InnerTestB {
@BeforeAll
void outerSetUp() {
actualOuterSetUp();
}
@AfterAll
void outerTearDown() {
actualOuterTearDown();
}
@BeforeEach
void innerSetUp() {
System.out.println(" innerSetUp");
}
@AfterEach
void innerTearDown() {
System.out.println(" innerTearDown");
}
@Test
void innerTest1() {
System.out.println(" innerTest1");
}
@Test
void innerTest2() {
System.out.println(" innerTest2");
}
@RepeatedTest(3)
void innerRepeatedTest() {
System.out.println(" innerRepeatedTest");
}
@ParameterizedTest
@ValueSource(strings = {"foo", "bar", "baz"})
void innerParameterizedTest(final String input) {
System.out.println(" innerParameterizedTest - " + input);
}
}
}
Output (order of the nested class may vary on your system):
outerSetUp
outerTest1
outerTearDown
outerSetUp
outerTest2
outerTearDown
outerSetUp
innerSetUp
innerRepeatedTest
innerTearDown
innerSetUp
innerRepeatedTest
innerTearDown
innerSetUp
innerRepeatedTest
innerTearDown
innerSetUp
innerParameterizedTest - foo
innerTearDown
innerSetUp
innerParameterizedTest - bar
innerTearDown
innerSetUp
innerParameterizedTest - baz
innerTearDown
innerSetUp
innerTest1
innerTearDown
innerSetUp
innerTest2
innerTearDown
outerTearDown
If you are on Java 16+, you can also omit @TestInstance(TestInstance.Lifecycle.PER_CLASS)
and use static
instead.
Upvotes: 0