JUnit 4 vs JUnit 5
JUnit 5 Improvements
- JUnit 5 leverages features from Java 8 or later, such as lambda functions, making tests more powerful and easier to maintain.
- JUnit 5 has added some very useful new features for describing, organizing, and executing tests. For instance, tests get better display names and can be organized hierarchically.
- JUnit 5 is organized into multiple libraries, so only the features you need are imported into your project. With build systems such as Maven and Gradle, including the right libraries is easy.
- JUnit 5 can use more than one extension at a time, which JUnit 4 could not (only one runner could be used at a time). This means you can easily combine the Spring extension with other extensions (such as your own custom extension).
Differences
Imports
org.junit.Test=>org.junit.jupiter.api.TestAssert=>Assertionsorg.junit.Assert=>org.junit.jupiter.api.Assertions
Annotations
@Before=>@BeforeEachorg.junit.Before=>org.junit.jupiter.api.BeforeEach
@After=>@AfterEach@BeforeClass=>@BeforeAll@AfterClass=>@AfterAll@Ignore=>@Disabledorg.junit.Ignore=>org.junit.jupiter.api.Disabled
@Category=>@Tag@RunWith,@Rule,@ClassRule=>@ExtendWithand@RegisterExtensionorg.junit.runner.RunWith=>org.junit.jupiter.api.extension.ExtendWith@RunWith(SpringRunner.class)=>@ExtendWith(SpringExtension.class)org.springframework.test.context.junit4.SpringRunner=>org.springframework.test.context.junit.jupiter.SpringExtension
Assertion Methods
JUnit 5 assertions are now in org.junit.jupiter.api.Assertions. Most of the common assertions, such as assertEquals() and assertNotNull(), look the same as before, but there are a few differences:
- The error message is now the last argument, for example:
assertEquals("my message", 1, 2)is nowassertEquals(1, 2, "my message"). - Most assertions now accept a lambda that constructs the error message, which is called only when the assertion fails.
assertTimeout()andassertTimeoutPreemptively()have replaced the@Timeoutannotation (there is an@Timeoutannotation in JUnit 5, but it works differently than in JUnit 4).- There are several new assertions, described below.
Note that you can continue to use assertions from JUnit 4 in a JUnit 5 test if you prefer.
Assumptions
Executes the supplied Executable, but only if the supplied assumption is valid.
JUnit 4
assumeThat("alwaysPasses", 1, is(1)); // passes |
JUnit 5
|
Extending JUnit
JUnit 4
// SpringRunner is an alias for the SpringJUnit4ClassRunner. |
JUnit 5
|
Expect Exceptions
JUnit 4
|
JUnit 5
|
Timeout
JUnit 4
|
JUnit 5
|
Converting a Test to JUnit 5
To convert an existing JUnit 4 test to JUnit 5, use the following steps, which should work for most tests:
- Update imports to remove JUnit 4 and add JUnit 5. For instance, update the package name for the
@Testannotation, and update both the package and class name for assertions (fromAssertstoAssertions). Don’t worry yet if there are compilation errors, because completing the following steps should resolve them. - Globally replace old annotations and class names with new ones. For example, replace all
@Beforewith@BeforeEachand allAssertswithAssertions. - Update assertions; any assertions that provide a message need to have the message argument moved to the end (pay special attention when all three arguments are strings!). Also, update timeouts and expected exceptions (see above for examples).
- Update assumptions if you are using them.
- Replace any instances of
@RunWith,@Rule, or@ClassRulewith the appropriate@ExtendWithannotations. You may need to find updated documentation online for the extensions you’re using for examples.
New Features
Display Names
you can add the @DisplayName annotation to classes and methods. The name is used when generating reports, which makes it easier to describe the purpose of tests and track down failures, for example:
|
Assertion Methods
JUnit 5 introduced some new assertions, such as the following:
assertIterableEquals() performs a deep verification of two iterables using equals().
void assertIterableEquals(Iterable<?> expected, Iterable> actual) |
assertLinesMatch() verifies that two lists of strings match; it accepts regular expressions in the expected argument.
void assertLinesMatch(List<String> expectedLines, List<String> actualLines) |
assertAll() groups multiple assertions together. Asserts that all supplied executables do not throw exceptions. The added benefit is that all assertions are performed, even if individual assertions fail.
void assertAll(Executable... executables) |
assertThrows() and assertDoesNotThrow() have replaced the expected property in the @Test annotation.
<T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable) |
void assertDoesNotThrow (Executable executable) |
Nested tests
Test suites in JUnit 4 were useful, but nested tests in JUnit 5 are easier to set up and maintain, and they better describe the relationships between test groups.
Parameterized tests
Test parameterization existed in JUnit 4, with built-in libraries such as JUnit4Parameterized or third-party libraries such as JUnitParams. In JUnit 5, parameterized tests are completely built in and adopt some of the best features from JUnit4Parameterized and JUnitParams, for example:
|
Conditional test execution
JUnit 5 provides the ExecutionCondition extension API to enable or disable a test or container (test class) conditionally. This is like using @Disabled on a test but it can define custom conditions. There are multiple built-in conditions, such as these:
@EnabledOnOsand@DisabledOnOs: Enables or disables a test only on specified operating systems@EnabledOnJreand@DisabledOnJre: Specifies the test should be enabled or disabled for particular versions of Java@EnabledIfSystemProperty: Enables a test based on the value of a JVM system property@EnabledIf: Uses scripted logic to enable a test if scripted conditions are met
Test templates
Test templates are not regular tests; they define a set of steps to perform, which can then be executed elsewhere using a specific invocation context. This means that you can define a test template once, and then build a list of invocation contexts at runtime to run that test with. For details and examples, see the documentation.
Dynamic tests
Dynamic tests are like test templates; the tests to run are generated at runtime. However, while test templates are defined with a specific set of steps and run multiple times, dynamic tests use the same invocation context but can execute different logic. One use for dynamic tests would be to stream a list of abstract objects and perform a separate set of assertions for each based on their concrete types. There are good examples in the documentation.
Spring Boot Test With JUnit
Spring Boot Test With JUnit 4
<dependency> |
|
Spring Boot Test With JUnit 5
<dependency> |
|
Conclusion
Although you probably won’t need to convert your old JUnit 4 tests to JUnit 5 unless you want to use new JUnit 5 features, there are compelling reasons to switch to JUnit 5.
References
Migrating from JUnit 4 to JUnit 5: Important Differences and Benefits
JUnit 5
JUnit 4