The much anticipated release of JUnit 5 is almost here and Spring 5.0 will have official support for it. However, it’s still possible to run JUnit 5 Tests within your current Spring applications right now. I’ll show you how to bring you testing to the next level by using Spring Boot and JUnit 5. This Post concentrates on the new ability to Dependency Inject right into our @Test method parameters. I warn you though, after getting a taste of this … there’s no going back!
Spring Boot and JUnit 5 Setup
I’ll be using the following artifacts in this post
- JUnit 5 M4 which has just been released (Note: GA release scheduled for Q3 2017)
- Spring Boot 1.5 (which uses Spring Framework 4.3)
Before we begin though, you may need a more formal introduction to JUnit 5 (nicknamed Jupiter since it’s the 5th planet from the Sun). Luckily, I have a YouTube Video Tutorial which covers just that.
Although I mention the JUnit M1 release, everything in there still applies (only exception being expectThrows method is deprecated in favor of assertThrows; I have updated its GitHub project for M4 and a video annotation was added).
Spring’s Parameter Resolver
JUnit 5 has an extensible architecture which can be taken advantage of via the @ExtendWith annotation. We’re no longer limited in using just one extension on a Test Class as was the case with JUnit 4 (think @Rule/@ClassRule) but rather, as many as needed.
One such extension point is the ParameterResolver which allows the possibility to dynamically resolve parameters at runtime; something that was not possible with earlier versions of JUnit.
Spring has implemented the ParameterResolver Interface and other JUnit Jupiter extension APIs via Class SpringExtension.
1 |
@ExtendWith(SpringExtension.class) |
In short, the SpringExtension.class integrates the Spring TestContext Framework into JUnit 5’s Jupiter programming model. It serves as a ParameterResolver implementation which resolves the requested parameter dependency against Springs ApplicationContext.
Parameter Resolver Options
Armed with SpringExtension, you’re now able to perform Dependency Injection into your Tests Class’s …
- Constructor
- Method parameters via either @Autowired, @Value or @Qualifier
- Methods annotated with@BeforeEach, @AfterEach, @BeforeAll and @AfterAll
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
@RunWith(JUnitPlatform.class) @SpringBootTest @ExtendWith(SpringExtension.class) public class SpringWithJUnit5IT { @Autowired Employee employee; Company company; @Autowired SpringWithJUnit5IT(Company company) { this.company = company; } @BeforeEach void beforeEach( @Autowired Company company) { this.company = company; } @Test @DisplayName("Checking if DI works with @Autowired on Constructor") void checkResolverForConstructor() { assertNotNull(company); assertTrue(company.getCompanyName().length() > 2); } @Test @DisplayName("Checking if DI works with @Autowired Parameter") void checkParamterReolverWithAutowired(@Autowired Employee awfulEmployee) { assertNotNull(awfulEmployee); assertEquals("homer", awfulEmployee.getName()); } @Test @DisplayName("Checking if DI works with @Qualifier and @Value Parameters") void checkParamterReolverWithQualifierAndValue( @Qualifier("mvpjava") Employee qualifiedEmployee, @Value("${employee.max.salary}") float maxSalary) { assertNotNull(qualifiedEmployee); assertAll("Validating new Employee properties", () -> assertTrue(qualifiedEmployee.getName().matches("^[a-zA-Z]+$")), () -> assertTrue(qualifiedEmployee.getSalary() >= maxSalary) ); } @Test @DisplayName("Checking that normal field DI works") void checkNormalSpingDI() { assertNotNull(employee); assertEquals("homer", employee.getName()); } } |
- @RunWith (JUnitPlatform.class) is a workaround to get JUnit 5 to work in this environment (more on this below)
- @SpringBootTest is my new preferred way to run an Integration Test in a Spring Boot Environment. It’s packed with fairy dust and If you need a more in depth explanation, then check out my post: Spring Boot Integration Testing (Slice and Dice).
Notice all the new Dependency Injection taking place in the Constructor, @BeforeEach and the @Test method input parameter? That’s new and awesome!
This now allows us to reduce the scope of out tests to a more local level. It also reduces the cognitive load as it keeps it all in one place without having to search/scroll up to see where your references are being setup. Me likey 🙂
Small Work Around – For now
In order to get JUnit 5 to work in this environment, we’ll have to do 2 things. First use …
1 |
@RunWith(JUnitPlatform.class) |
which launches JUnit 5 tests on top of JUnit 4. This is a small workaround that is needed until built in support is available.
Annotating a class with @RunWith(JUnitPlatform.class) allows it to be run with IDEs and build systems that support JUnit 4 but do not yet support the JUnit Platform directly.
–JUnit 5 User Guide
Second, declare the correct project dependencies in order to include the spring-test-junit5 jar. Since we are test driving this out before the downloadable jar is available (not available in a public repository). You can either
- Install it in local repository
- Use JitPack to get a hold of the jar.
I’ve made it easy for you. You can get the working code from MVP Java’s GitHub account (I opted for JitPack via maven).
Can It Get Any Better?
Yes! Lets add another extension and start using the Parameter Resolving capabilities of the MockitoExtension. Here’s an example which uses both Extensions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@RunWith(JUnitPlatform.class) @SpringBootTest @ExtendWith(SpringExtension.class) @ExtendWith(MockitoExtension.class) public class SpringWithJUnit5MultipleExtensionsT1 { @Test @DisplayName("Checking if ParameterResolver works for @Autowired and @Mock") void checkMultipleParameterResolvers( @Autowired Company company, @Mock Employee newHire) { assertTrue(company.addEmployee(newHire)); } } |
How cool is that!? Now your ready to get ahead of the curve. Happy testing.
Want To See It In Action via YouTube Video Tutorial?
Shout out to Sam Brannen who has been instrumental in integrating Spring with JUnit and Marc Philippe for developing the Mockito Extension.
Code available on GitHub.