Recently, I was looking for a way to move from JUnit4 to JUnit5, but I found it difficult to search for materials here and there, so I decided to summarize it.
For a small project, I think it's possible to change the notation from JUnit4 to JUnit5 at once, but for a large project, the quantity is strict, so basically I think it's stable to proceed as follows. ..
The second one above works as long as junit-vintage-engine is in the dependent library, but I think it will come when I have to do it, so at any time.
By the way, the official migration method of JUnit is → https://junit.org/junit5/docs/current/user-guide/#migrating-from-junit4
First, switch to the JUnit5 library (the following is Maven, but in the case of Gradle, please change it appropriately [^ gradle]) [^ gradle]: For Gradle settings, refer to the official https://github.com/junit-team/junit5-samples/tree/main/junit5-migration-gradle.
I think it was originally written in JUnit4 as follows.
pom.xml(JUnit4)
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
Instead of the above content, put the following
pom.xml(JUnit5)
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
By the way, I'm using InteliJ, so I added the following
(Just looking at the official message Only needed to run tests in a version of IntelliJ IDEA that bundles older versions, I wondered if the new InteliJ wouldn't have to be included.
pom.xml(JUnit5)
<!-- Only needed to run tests in a version of IntelliJ IDEA that bundles older versions -->
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>1.7.0</version>
<scope>test</scope>
</dependency>
By the way, the meaning of each is as follows with reference to Official explanation. It feels like I'll add others when I need it.
Personally, it was quite easy for JUnit4's @Rule to disappear.
I used to use the one under org.junit in JUnit4, but in JUnit5 it becomes the one under org.junit.jupiter.api, so simply rewrite the Package of import.
| Target | JUnit4 Package | JUnit5 Package |
|---|---|---|
@TestAnd |
org.junit.* | org.junit.jupiter.api.* |
fail()And |
org.junit.Assert.* | org.junit.jupiter.api.Assertions.* |
The annotation has changed, so replace it
| JUnit4 | JUnit5 |
|---|---|
@Before |
@BeforeEach |
@After |
@AfterEach |
@BeforeClass |
@BeforeAll |
@AfterClass |
@AfterAll |
@Ignore |
@Disabled |
@Category |
@Tag |
@BeforeClass or @AfterClass due to test hierarchy etc., a little more annotation is required, so I will write it separately below)In addition, the ones that can not be rewritten simply are as follows.
--Switching from @ RunWith to @ ExtendWith
--Switching from @Rule or @ClassRule to @ExtendWith or @RegisterExtension
I think JUnit4 used inner class and @ RunWith (Enclosed.class) to organize the tests (like below).
How to organize JUnit4 tests
@RunWith(Enclosed.class)
public class SampleJUnit4Test {
public static class TestCategoryA {
@Test
public void testA1() { ... }
}
public static class TestCategoryB {
@Test
public void testB1() { ... }
}
}
JUnit5 allows hierarchies with @ Nested and non-static classes.
I wondered if the notation became straightforward (impression).
How to organize JUnit 5 tests
public class SampleJUnit5Test {
@Nested
class TestCategoryA {
@Test
public void testA1() { ... }
}
@Nested
class TestCategoryB {
@Test
public void testB1() { ... }
}
}
As a caveat, if you add @Nested during the migration process from JUnit4 to JUnit5 but keep it as a static class, a compile error will not occur, but the test execution will be ignored. Please be careful. (I haven't checked the details)
I think I wrote the following in the foreground,
- However, if you use @BeforeClass or @AfterClass in the inner class due to test hierarchy etc., you need a little more annotation, so I will write it separately below)
When migrating JUnit5, the inner class will no longer be static by using a test hierarchy using @ Nested. Therefore, if you do @BeforeClass or @AfterClass in the inner class, an error will occur.
In this case, if you add @TestInstance (Lifecycle.PER_CLASS) to the target class according to the contents of Official description, a test instance will be created for each test class, and in the inner class You will be able to use @BeforeAll and @AfterAll.
Use All system in inner class in JUnit5
public class SampleJUnit5Test {
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class TestCategory {
@BeforeAll
static void beforeAll() { ... }
}
}
Since the Assert method of Exception type has changed, I will change it as well.
In JUnit4, I think I used @Test (expected = SampleException.class) and ExpectedException.
For JUnit 4
@Test(expected = NullPointerException.class)
public void NullPointerException occurs() {
/*Tests that expect a NullPointerException*/
}
For JUnit 4
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Test
public void SampleException occurs() {
expectedException.expect(SampleException.class);
expectedException.expectMessage("SampleException message");
/*Tests that expect SampleException to occur*/
}
For JUnit5, use assertThrows.
In the argument of assetThrows, pass the class of Exception that is expected to occur and the test code (via lambda expression).
For JUnit 5
@Test
public void NullPointerException occurs() {
assertThrows(NullPointerException.class, () -> {
/*Tests that expect a NullPointerException*/
});
}
If you only want to check the occurrence of exceptions, you can use only assertThrows, but if you want to test the details of the Exception that occurred including the error message, receive the Exception returned by assertThrows and write various validations.
For JUnit 5
@Test
public void SampleException occurs() {
SampleException exception = assertThrows(SampleException.class, () -> {
/*Tests that expect SampleException to occur*/
});
assertEquals("SampleException message", exception.getMessage());
}
In JUnit4, I think there is a place where the test is written using @Rule + TemporaryFolder for the test around the file. In JUnit5, @Rule is gone, so this is also a fix.
It's very simple, though, and what was written in JUnit4 as follows
python
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
private Path root;
@Before
public void setup() {
root = temporaryFolder.getRoot().toPath().toAbsolutePath();
}
Change it to the following (Note that variables with @ TempDir will cause an error if they are private)
python
@TempDir
Path root;
I feel that this is a good feeling because the annoying things have disappeared.
@ RunWith (Theories.class) + @ DataPoint + @ Theory)@ParameterizedTest + @ValueSource if the repeat target is a primitive type@ParameterizedTest + @ MethodSourceRecommended Posts