Parameterized beforeEach/beforeAll in JUnit 5
Asked Answered
P

2

8

I want to write a test for a small database-like application. This application uses queries and a query should return the correct result. This is easily implemented in JUnit 5, something like

@BeforeEach
void before() {
  database = prepareDatabase();
}

@Test
void testQuery1() {
  assertThat(database.query("query1")).isEqualTo("result1");
}

@Test
void testQuery2() {
  assertThat(database.query("query2")).isEqualTo("result2");
}

....

Now I want to add optimization switches (e.g. a query optimizer or database indices). The query should return the same results regardless of the running optimization switch (optimizations should only change efficiency not results).

For the test this means that I want to run the same methods for slightly other implementations of prepareDatabase() (e.g. one with optimizer, one with index, one with nothing).

I did not yet find a proper extension for this. I thought of duplicating the whole class for each optimization setting or providing the methods from a shared parent class. Yet this does not feel like the JUnit 5 way of doing this task. Maybe someone could point me to a feature that could help me with this problem?

Pigheaded answered 3/8, 2018 at 18:55 Comment(0)
P
2

The missing JUnit5 feature is called "Container Templates" and is a scheduled feature request at https://github.com/junit-team/junit5/issues/871.

Pigheaded answered 19/8, 2020 at 18:30 Comment(0)
J
2

As always, there is multiple possible ways of achieving your goal. One of them is to use Java inheritance. You don't even need to wire up any advanced JUnit 5 feature, just an old Java concepts.

Create abstract class with your methods and abstract prepareDatabase().

public abstract class QueryTest {

    Database database;

    @BeforeEach
    void before() {
        database = prepareDatabase();
    }

    @Test
    void testQuery1() {
        assertThat(database.query("query1"), is("result1"));
    }

    @Test
    void testQuery2() {
        assertThat(database.query("query2"), is("result2"));
    }

    abstract Database prepareDatabase();

}

Then - based on different variants, create implementations, overriding only this methods.

public class SwitchTrueQueryTest extends QueryTest {

    @Override
    Database prepareDatabase() {
        return new Database(true);
    }

}

public class SwitchFalseQueryTest extends QueryTest {

    @Override
    Database prepareDatabase() {
        return new Database(false);
    }

}

That's it. Nothing more is necessary, reusable tests are in parent class, no need to duplicate.

Report example

Jeb answered 4/8, 2018 at 15:13 Comment(2)
Downvoting because this answer makes the implementation very limiting. 1. you can only extend one class and 2. this will make tests highly coupledDyan
@Jezor, in this implementation SwitchTrueQueryTest and SwitchFalseQueryTest act as "parameterized" test subclasses. They are the workaround which provides the test parameterization. So I think neither your point 1, nor point 2 are really an issue with this because you would normally not include any further test methods in these subclasses (unless they are specific to one "parameter" value). But yes, if you have multiple parameters you want to tweak for the test, this approach is not ideal because it would lead to a lot of subclasses.Shouse
P
2

The missing JUnit5 feature is called "Container Templates" and is a scheduled feature request at https://github.com/junit-team/junit5/issues/871.

Pigheaded answered 19/8, 2020 at 18:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.