I am writing some JUnit tests with JUnit 5. Before each test I need to load a test resource and initialize some other stuff with it. For that I wrote my init method annotated with @BeforeEach
, because that progress is always the same except that the resource to load should be a different one for each test.
I first thought of removing the @BeforeEach annotation, add a parameter to the init method to specify which resource should be loaded and call the init method myself from within each test at the beginning.
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
public class MyTest {
private Object testResource;
private void init(String resourcePath) {
// actually load the resource and initialize some fields here
this.testResource = "Loaded resource from: " + resourcePath;
}
@Test
public void test0() {
init("TestResource0");
assertEquals("Loaded resource from: TestResource0", testResource);
}
@Test
public void test1() {
init("TestResource1");
assertEquals("Loaded resource from: TestResource1", testResource);
}
}
That way I feel like I am undermining the whole structure of the JUnit test flow and I fear, that that could cause some issues in the future when extending the tests, by e.g. some fancy meta test programming, where I'm gonna rely on JUnit to provide the correct information about the current state of the test.
So I decided to keep the parameter for the resource to load on the init method and keep the @BeforeEach
annotation. That way I needed to include a ParameterResolver
. My first thought about the implementation of resolveParameter(ParameterContext, ExtensionContext)
was to first find out what test is about to be executed and return the corresponding resource to be loaded for that test.
static class MyParamResolver implements ParameterResolver {
@Override
public Object resolveParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) throws ParameterResolutionException {
String test = extensionContext.getRequiredTestMethod()
.getName();
switch (test) {
case "test0":
return "TestResource0";
case "test1":
return "TestResource1";
}
throw new ParameterResolutionException("Unknown test " + test);
}
@Override
public boolean supportsParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) {
return true;
}
}
I don't like that solution, because I first dont have safety for the names to be correct ensured by the compiler and secondly the resources to be loaded are not obviously connected to the corresponding test at first sight. I then thought it would be nice to provide the resource path somehow to an annotation, that is directly attached to the test method. Something like that:
@Test("test0")
public void test0() {
assertEquals("Loaded resource from: TestResource0", testResource);
}
@Test("test1")
public void test1() {
assertEquals("Loaded resource from: TestResource1", testResource);
}
...
@Override
public Object resolveParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) {
return extensionContext.getRequiredTestMethod()
.getAnnotation(Test.class)
.value();
}
Unfortunately the @Test
annotation doesn't define any parameters and after some research in the JUnit docs I couldn't find any other annotation fitting in here. The best thing I can think of now is to create my own annotation and putting it on each test method. But I also think that this is a common problem, which can be solved without reinventing the weel myself. I just couldn't find anything.
Is there a convenient way to solve this problem, that doesn't need to invent own annotations. In other words: Is there an easy way, that keeps up the code quality and readability to solve this problem by just using the framework API that is already there?
ParameterizedTest
is useful when you have the same steps for a test, but want to execute it with different parameters. That doesn't seem to be the case. You can simply use the TestInfo
parameter to the @BeforeEach
method to handle your case.
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
public class JUnitTest {
@Test public void test1() {
System.out.println("Test");
}
@BeforeEach public void f(TestInfo info) {
System.out.println(info.getDisplayName());
}
}
I guess, you're looking for "container templates". They are not part of Jupiter, yet. But already scheduled for 5.4: https://github.com/junit-team/junit5/issues/871
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With