JUnit5 TestSuite Alternative
JUnit4 had the TestSuite
class to aggregate multiple tests. This is not available in JUnit 5. Generally test discovery via a bunch of named tests in a suite somewhat sucks. However, if the aim is not test-discovery, but the sharing of resources between different test classes, then it makes sense to want to create a parent.
JUnit 5 provides the @Nested
annotation to allow a child class to run inside the context of its parent. The assumption is that the child class is non-static, so has access to the instance values of its parent. If we want to share test resources, we probably want to think about the class-level setup of the test suite, and somehow wiring that into the class-level setup of our child classes.
Let’s contrive a fake example that demonstrates the problem:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 | @Testcontainers // use docker images class MyTest { // make a DB at the start of the test in a docker container // takes a few minutes to boot up @Container private static final DatabaseContainer DB = createDbContainer(); private static MyDao dao; @BeforeAll static void beforeAll() { dao = createDaoFrom(DB); } @Test void daoFeatureOne() { assertThat(dao.find( "no data" )).isEmpty(); } } |
The above is a test which starts up a database in the global lifecycle of the test class. It wires up a dao
object to it, and can have multiple tests that reuse that dao
.
In an ideal world we could reset everything for each test, but a database is an expensive resource to start up. Maybe we can add some beforeEach
and afterEach
hooks to clean its data, but we wouldn’t want to bounce the database. Each time. Similarly, some framework startup cost for our dao
may be undesirable if run every time.
The above, as the one and only test in our project would be fine, but what if there are other tests that also need this database… and what if it really takes AGES to run…
There’s No Suite in JUnit 5
Annoying isn’t it. If only we could do:
1 2 3 4 5 6 7 8 | @JUnit5TestSuite // not real @Children ({MyDaoTest. class , MyOtherDaoTest. class }) @Testcontainers class MyTestSuite { @Container private static final DatabaseContainer DB = createDbContainer(); } |
That would be brilliant… but it would leave us with some questions:
- How do we ensure the child tests don’t run outside of the suite?
- How do these tests access the `DB` object?
A Sort of Suite Alternative
Let’s imagine that we have a static method getDb
to provide the database when we need it.
Now let’s rewrite the original DaoTest to use it, and make it abstract so the test runner won’t pick it up:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 | abstract class MyTestImpl implements DbProvider { private static MyDao dao; @BeforeAll static void beforeAll() { // access to the database container // from the static method (statically imported) dao = createDaoFrom(getDb()); } @Test void daoFeatureOne() { assertThat(dao.find( "no data" )).isEmpty(); } } |
Now we’ve got a partial test that could be run in a suite, let’s define the suite. Let’s also use @Nested
to wire in the child class:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 | @Testcontainers // use docker images class MyTest { // make a DB at the start of the test in a docker container // takes a few minutes to boot up @Container private static final DatabaseContainer DB = createDbContainer(); // provide the getDb function to access the container public static DatabaseContainer getDb() { return DB; } // test suite members are just nested classes that extend // the abstract class of each member of the suite @Nested class MyTest extends MyTestImpl { } // ... add more suite members with more @Nested } |
Disadvantages
With classes operating on each other’s static bits, this runs the risk of getting confusing.
The fact that each nested class needs to be a subclass is a bit funky too…
But this works and makes for an effective test suite.
Published on Java Code Geeks with permission by Ashley Frieze, partner at our JCG program. See the original article here: JUnit5 TestSuite Alternative Opinions expressed by Java Code Geeks contributors are their own. |
It does now.
https://junit.org/junit5/docs/current/user-guide/#running-tests-junit-platform-runner-test-
suite
That is for running JUnit5 tests under JUnit4 and doesn’t provide the lifecycle/shared resources that are described in the above article.