I’ve been extending JUnit to provide project-specific testing infrastructure for quite some time, and have always been disappointed with the extension mechanisms prior to JUnit 5.
To begin with, JUnit 4’s primary extension mechanism was the actual test runner. If you wanted to mixin multiple extensions, you were in for a hassle. For one test-infrastructure-project, I would have the runner dynamically subclass the test case and add behaviors based on the infrastructure-specific annotations that were present. This worked really well, except that most IDEs I tried would not run the tests correctly at the method level, so you’d be left re-running the entire class.
On another project, I jumped through all manner of hoops to avoid this. Spring-test alleviated many of the problems with their TestExecutionLisiteners
, but not all, and it was less lovely than subclassing in use. The result worked quite well, but it was pretty excruciating to write. Now that we’re rewriting everything from scratch, I’m implementing our testing infrastructure on JUnit 5 and Spring 5, and JUnit 5 makes this awesome!
How’s it work?
JUnit 5 provides a new extension mechanism called, well, an Extension. Extension
is a marker-interface with specializations for each portion of the test lifecycle. For instance, the interface BeforeAll
will equip you with a beforeAll
method which is called with the container extension context, providing, among other things, the test class.
For testing purposes, we need to inject and override properties pretty routinely. Spring 5’s YAML support isn’t there when used with @TestPropertySource
, so I thought I’d whip up my own (full source)
Then, to use it (given a resource-structure) suchlike:
Properties Extension Usage
@RunWith(JUnitPlatform.class) @ExtendWith({ SpringExtension.class, PropertiesExtension.class }) @Properties({ "classpath:classes/frap.yml" }) @ContextConfiguration(classes = PropertiesExtensionConfiguration.class) public class PropertiesExtensionTest { @Inject private Environment environment; @Value("${classes.properties.frap}") String frap; @Test public void ensureInjectingPropertiesWorks() { assertThat(frap, is("lolwat")); } @Test public void ensureLoadingYamlAtClassLevelWorks() { String prop = environment.getProperty("classes.properties.frap"); assertThat(prop, is("lolwat")); } @Test @Properties("classpath:methods/frap.yml") public void ensureOverridingOnMethodWorksWithInjection() { String prop = environment.getProperty("classes.properties.frap"); assertThat(prop, is("overridden")); } @Test @Properties("classpath:methods/frap.yml") public void ensureOverridingOnMethodWorks() { assertThat(frap, is("overridden")); } }