Junit 4, the famous Java unit testing framework, has been out for a long time. At that time, I found that JUnit 5 was in beta version, so I was going to write an article to introduce JUnit 5. However, because it is still a beta version, some parts are not perfect, and I am a little lazy and not writing them well. I suddenly remembered this incident these days. When I checked it on the official website, I found that on September 10, the official version of JUnit 5 finally came out! Then I happened to write the article again and introduce the latest JUnit framework to you.
Framework structure
Compared with JUnit 4, the structure of JUnit 5 is very clear and provides good support for extended functions such as custom plug-ins, IDE test execution, etc. This can be seen from the project structure.
JUnit Platform
The package name of this group is org.junit.platform. As you can see from the name, the main function of this group is to serve as the basic platform for the testing framework. The modules under this package include basic APIs, execution engines and executors, basic command line execution functions, command line interfaces, Maven and Gradle test plug-ins and other basic functions.
JUnit Jupiter
Jupiter is the code name of JUnit 5. The modules under this package contain the main functions of JUnit 5. If we want to use JUnit 5, we must include this set of modules.
JUnit Vintage
Vintage is the code name for the old version of JUnit. The modules under this package allow us to run tests of old JUnit 3 and 4 on the new JUnit platform.
Import class library
While JUnit 5 is still in the beta phase, there are examples of integrating JUnit 5 in Maven and Gradle in the official documentation. But in the official version, the content of this part disappeared, leaving only two links to the sample project, let's refer to it (copy and paste).
Using Maven
junit5-maven-consumer is the official Maven example. I was originally going to post the relevant POM configuration here, but when I saw that Maven's configuration was too long, I'd better forget it. If you have any requirements, please check the POM configuration of this project yourself.
Using Gradle
If you use Gradle, then this problem is much simpler. There are also detailed descriptions in the junit5-gradle-consumer sample project.
First of all, Gradle does not support JUnit 5 by default, so the JUnit Platform Gradle plug-in needs to be enabled to support it.
buildscript { repositories { mavenCentral() } dependencies { classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.0' }}apply plugin: 'org.junit.platform.gradle.plugin'Then there is the configuration of this Gradle plugin. By default, all engines and tags will be executed. If you want to choose to perform tests for only certain engines and tags, you can uncomment the following and modify them according to your own needs. Of course, if you don’t have these advanced requirements, you can delete this part.
junitPlatform { // platformVersion '1.0.0' filters { engines { // include 'junit-jupiter', 'junit-vintage' // exclude 'custom-engine' } tags { // include 'fast' exclude 'slow' } // includeClassNamePattern '.*Test' } // enableStandardTestTask true // reportsDir file('build/test-results/junit-platform') // this is the default // logManager 'org.apache.logging.log4j.jul.LogManager'}If you only need to run JUnit 5 tests, just import the following two dependencies. JUnit Platform dependencies are automatically imported.
dependencies { testCompile("org.junit.jupiter:junit-jupiter-api:5.0.0") testRuntime("org.junit.jupiter:junit-jupiter-engine:5.0.0")}If you want to run old JUnit 3 and 4 tests under the new platform, you need to import the following dependencies.
dependencies { testCompile("junit:junit:4.12") testRuntime("org.junit.vintage:junit-vintage-engine:4.12.0")}Write tests
JUnit 4 Test
If all the previous configuration is complete, you can start writing tests now. First, let’s review the old JUnit 4 test.
public class JUnit4Test {@BeforeClass public static void init() {System.out.println("Before Class");}@AfterClass public static void clean() {System.out.println("After class");}@Before public void before() {System.out.println("Before");}@After public void after() {System.out.println("After");}@Test public void test1() {System.out.println("Test 1");}@Test public void test2() {System.out.println("Test 2");}}Use gradle test and other commands to execute this test. The result is similar to this.
Before ClassBeforeTest 1Test 2After class
JUnit 5 Test
Let's take a look at how to write the equivalent JUnit 5 test. The most obvious change can be seen: first of all, several annotations were renamed to more famous names; another point is that the test method does not have to be a public method, so we can type the keyboard a few less times.
public class JUnit5Test {@BeforeAll static void beforeAll() {System.out.println("Before All");}@AfterAll static void afterAll() {System.out.println("After All");}@BeforeEach void before() {System.out.println("Before");}@AfterEach void after() {System.out.println("After");}@Test void test1() {System.out.println("Test 1");}@Test void test2() {System.out.println("Test 2");}}Write assertions
To verify that the test cases are correct, we need to write some assertions. JUnit 5 comes with many assertions that can help us write test cases. Moreover, these assertions come with overloaded versions that can accept lambda expressions, which are very suitable for Java 8 use. Of course, I personally think it is more convenient to assertJ.
import static org.junit.Assert.assertTrue;import static org.junit.jupiter.api.Assertions.*;public class AssertionDemo { @Test void testAssertion() { assertEquals(10, 10); assertTrue(true); assertEquals(100, 100, "two equal numbers"); assertAll("number" , () -> assertEquals("name", "name") , () -> assertEquals(500, 500)); assertThrows(InvalidParameterException.class , () -> { throw new InvalidParameterException(); } ); int result = assertTimeout(Duration.ofSeconds(5) , () -> { int i = 0, j = 0; while (i <= 100) { for (; j <= 100000; j++) j++; i++; } return i; }); assertEquals(100, result); }}Dependency injection
Now both the constructor and test methods of the test class can accept parameters. The ParameterResolver interface defines how to inject parameters at runtime. Several built-in allows us to get information about the runtime of the test case.
First is TestInfoParameterResolver. If there is an instance of type TestInfo on the method, the JUnit 5 framework will automatically inject the instance. Several methods of this instance can allow us to obtain the name, display name, label and other information of the test class and test method.
public class DependencyInjectionDemo { @Test @DisplayName("Dependency Injection") @Tag("test") void testDisplayName(TestInfo testInfo) { assertEquals("Dependency Injection", testInfo.getDisplayName()); assertEquals(Collections.singleton("test"), testInfo.getTags()); }}There are also built-in parameter parsers such as RepetitionInfoParameterResolver, which will be introduced later.
Common annotations
display name
We can add custom names to test classes and test methods, which are displayed by the test runner and test reports. The display name does not display like a variable name. It can be a long string containing spaces, or even an Emoji emoji.
@DisplayName("The test class can specify the display name") public class DisplayNameDemo {@Test @DisplayName("The test method can also specify the display name") void testWithLongDisplayName() {}@Test @DisplayName("The display name can also contain emoticons ��") void testWithDisplayNameWithEmoji() {}}Disable testing
@Disabled annotation can be used on test classes or test methods, and the corresponding test can be disabled.
@Disabledpublic class DisabledTestDemo { @Test //@Disabled void testDisabled() { }}Repeat test
If you need to have a test method run multiple times, use the @RepeatedTest annotation.
public class RepeatedTestDemo { @RepeatedTest(10) void testRepeated10Times() { }}You can also inject an instance RepetitionInfo to check the current number of repetitions and the total number of repetitions.
public class RepeatedTestDemo {@BeforeEach void beforeEach(RepetitionInfo info) {System.out.printf("%d - %d/n" , info.getCurrentRepetition() , info.getTotalRepetitions());}@RepeatedTest(10) void testRepeated10Times() {}}Comes with tags
When I introduced the configuration of Gradle, I said that you can choose to filter certain labels in the configuration. It is also very simple to give tags in the code, just use @Tag annotation.
@Tag("taggedTest")public class TagDemo {@Test @Tag("taggedTest1") void testWithTag1() {}@Test @Tag("taggedTest2") void testWithTag2() {}}Nested Tests
Sometimes nested tests may be required to indicate the inclusion relationship between certain tests. Nested tests use @Nested annotation.
@DisplayName("outer test")public class NestedDemo { @Test void testOuter() { } @Nested @DisplayName("inner test") class InnerTestDemo { @Test void testInner() { } }}It should be noted that only static inner classes can use Nested annotations. In addition, since Java does not allow internal classes to have static methods, it cannot have @BeforeAll and @AfterAll annotations. If you want to break through this limitation, you need to add the @TestInstance(Lifecycle.PER_CLASS) annotation to the nested inner class. For details, please refer to Test Instance Lifecycle.
IDE support
Although JUnit 5 has come out now. However, the support of various toolchains has not kept up. Currently only Intellij IDEA and Eclipse 4.7 (Oxygen) have added support for JUnit 5. So if you are in a formal situation, it is more safe to use JUnit 4.
Frequently Asked Questions
Distinguish between different versions of @Test annotation
Just as I was writing this article, I encountered a problem with my test example. The test could not be passed and the error message below is displayed.
Failures (1): JUnit Vintage:yitian.study.test.AssertionDemo:initializationError ClassSource [className = 'yitian.study.test.AssertionDemo', filePosition = null] => java.lang.Exception: Method testAssertion() should be public
Students with good English should be able to recognize it. This error message says that the test method must be public. But as mentioned earlier, JUnit 5 has cancelled this restriction, so why does this error still occur? I looked carefully and found the error. Maybe it is because JUnit 4 was used a lot in the past, so the IDE defaults to the @Test annotation, which is automatically completed.
import org.junit.Test;
This package is the @Test annotation under JUnit 4. If we want to use JUnit 5, we need the following @Test annotation.
import org.junit.jupiter.api.Test;
After modifying it, run the test again, and there was indeed no problem. Of course, for the sake of learning and use, I also reference the JUnit 4 package, so this conflict occurs. If you don't have any special needs, it is recommended to import only JUnit 5 jar packages to prevent confusion. Of course, importing them all is OK, but you need to be careful to distinguish them and do not write JUnit 4 annotations to JUnit 5 tests. Finally, I will attach my test examples. Those who are interested can take a look.
Summarize
The above is the entire content of this article about JUnit5 related content, and I hope it will be helpful to everyone. Interested friends can continue to refer to other related topics on this site. If there are any shortcomings, please leave a message to point it out. Thank you friends for your support for this site!