catalogue
https://www.yuque.com/atguigu/springboot/ksndgx
1, JUnit 5 changes
1.1 basic concepts
Spring boot version 2.2.0 began to introduce JUnit 5 as the default library for unit testing. As the latest version of JUnit framework, JUnit 5 is very different from the previous version of JUnit framework.
JUnit 5 consists of several different modules from three different subprojects.
- JUnit Platform - the basis for starting the test framework on the JVM. It supports not only Junit's self-made test engine, but also other test engines.
- JUnit Jupiter - is the core of JUnit 5's new features. It contains a test engine for running on Junit Platform.
- JUnit Vintage - provides JUnit 4.0 compatibility x, Junit3. X test engine.
be careful:
Springboot versions above 2.4 remove the default dependency on Vintage. If you need to be compatible with junit4, you need to introduce it yourself (you can't use the function @ Test of junit4)
JUnit 5's vintage engine removed from spring boot starter test. If you need to continue to be compatible with junit4, you need to introduce vintage by yourself
<dependency> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-core</artifactId> </exclusion> </exclusions> </dependency>
1.2 test case writing method
For the current version, write the single test method
@SpringBootTest class Boot05WebAdminApplicationTests { @Test void contextLoads() { } }
In the old version period, the following notes are required for single test:
@SpringBootTest + @RunWith(SpringTest.class)
1.3 introducing dependencies
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
This dependency introduces the following packages:
2, JUnit 5 common annotations
JUnit 5 annotations are different from JUnit 4 annotations
Official documents: https://junit.org/junit5/docs/current/user-guide/#writing-tests-annotations
- @Test -- the representation method is the test method. However, unlike JUnit4's @ test, it has a very single responsibility and cannot declare any attributes. Jupiter will provide additional tests for expanded tests
- @ParameterizedTest -- the representation method is parameterized test, which will be described in detail below
- @RepeatedTest -- indicates that the method can be executed repeatedly, which will be described in detail below
- @DisplayName -- set the display name for the test class or test method
- @Before each -- execute before each unit test
- @After each -- after each unit test
- @Before all -- indicates that all unit tests are executed before
- @After all -- means to execute after all unit tests
- @Tag -- indicates the unit test category, similar to @ Categories in JUnit4
- @Disabled -- indicates that the test class or test method is not executed, similar to @ Ignore in JUnit 4
- @Timeout -- indicates that the test method will return an error if it runs beyond the specified time
- @ExtendWith -- provides an extension class reference for a test class or test method
3, assertions
Assertions are the core part of the test method, which is used to verify the conditions that the test needs to meet. These assertion methods are org junit. jupiter. api. Static method of assertions.
The built-in assertions of JUnit 5 can be divided into the following categories:
3.1 simple assertion
Used for simple validation of a single value. For example:
method | explain |
assertEquals | Determines whether two objects or two primitive types are equal |
assertNotEquals | Judge whether two objects or two original types are not equal |
assertSame | Judge whether two object references point to the same object |
assertNotSame | Judge whether two object references point to different objects |
assertTrue | Determines whether the given Boolean value is true |
assertFalse | Determines whether the given Boolean value is false |
assertNull | Determines whether the given object reference is null |
assertNotNull | Determines whether the given object reference is not null |
3.2 array assertion
Use the assertArrayEquals method to determine whether two objects or arrays of the original type are equal
@Test @DisplayName("array assertion") public void array() { assertArrayEquals(new int[]{1, 2}, new int[] {1, 2}); }
3.3 combined assertions
The assertAll method accepts multiple org junit. jupiter. api. As the assertions to be verified, the instance of the executable functional interface can easily provide these assertions through lambda expressions
@Test @DisplayName("assert all") public void all() { assertAll("Math", () -> assertEquals(2, 1 + 1), () -> assertTrue(1 > 0) ); }
3.4 exception assertion
In JUnit 4, when you want to test the exception of a method, the ExpectedException variable annotated with @ Rule is still troublesome. JUnit 5 provides a new way of assertions Assertthrows() can be used in conjunction with functional programming.
@Test @DisplayName("Abnormal test") public void exceptionTest() { ArithmeticException exception = Assertions.assertThrows( //Throw assertion exception ArithmeticException.class, () -> System.out.println(1 % 0)); }
3.5 timeout assertion
Junit5 also provides assertions Asserttimeout() sets a timeout for the test method
@Test @DisplayName("Timeout tests ") public void timeoutTest() { //If the test method takes more than 1s, it will be abnormal Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(500)); }
3.6 rapid failure
The test fails directly through the fail method
@Test @DisplayName("fail") public void shouldFail() { fail("This should fail"); }
4, Preconditions
Preconditions in JUnit 5 are similar to assertions. The difference is that unsatisfied assertions will fail the test method, while unsatisfied preconditions will only terminate the execution of the test method. Preconditions can be regarded as the premise of test method execution. When the premise is not met, there is no need to continue execution.
@DisplayName("Preconditions") public class AssumptionsTest { private final String environment = "DEV"; @Test @DisplayName("simple") public void simpleAssume() { assumeTrue(Objects.equals(this.environment, "DEV")); assumeFalse(() -> Objects.equals(this.environment, "PROD")); } @Test @DisplayName("assume then do") public void assumeThenDo() { assumingThat( Objects.equals(this.environment, "DEV"), () -> System.out.println("In DEV") ); } }
assumeTrue and assumFalse ensure that the given condition is true or false. If the condition is not met, the test execution will be terminated. The parameter assemingthat is the Boolean value representing the condition and the implementation object of the corresponding Executable interface. The Executable object will be executed only when the conditions are met; When the conditions are not met, the test execution does not terminate.
5, Nested test
JUnit 5 can implement Nested tests through internal classes and @ Nested annotations in Java, so as to better organize relevant test methods together.
The @ BeforeEach and @ AfterEach annotations of the outer layer can be used in the inner class, while the outer layer cannot drive the @ BeforeEach and @ AfterEach annotations of the inner layer
@DisplayName("A stack") class TestingAStackDemo { Stack<Object> stack; @Test @DisplayName("is instantiated with new Stack()") void isInstantiatedWithNew() { new Stack<>(); } @Nested @DisplayName("when new") class WhenNew { @BeforeEach void createNewStack() { stack = new Stack<>(); } @Test @DisplayName("is empty") void isEmpty() { assertTrue(stack.isEmpty()); } @Test @DisplayName("throws EmptyStackException when popped") void throwsExceptionWhenPopped() { assertThrows(EmptyStackException.class, stack::pop); } @Test @DisplayName("throws EmptyStackException when peeked") void throwsExceptionWhenPeeked() { assertThrows(EmptyStackException.class, stack::peek); } @Nested @DisplayName("after pushing an element") class AfterPushing { String anElement = "an element"; @BeforeEach void pushAnElement() { stack.push(anElement); } @Test @DisplayName("it is no longer empty") void isNotEmpty() { assertFalse(stack.isEmpty()); } @Test @DisplayName("returns the element when popped and is empty") void returnElementWhenPopped() { assertEquals(anElement, stack.pop()); assertTrue(stack.isEmpty()); } @Test @DisplayName("returns the element when peeked but remains not empty") void returnElementWhenPeeked() { assertEquals(anElement, stack.peek()); assertFalse(stack.isEmpty()); } } } }
6, Parametric test
Parametric testing is a very important new feature of JUnit 5. It makes it possible to run tests multiple times with different parameters, and also brings a lot of convenience to our unit testing.
Of course, if the parametric test can only specify ordinary input parameters, it can not reach the point that makes me feel amazing. What makes me really feel his strength is that he can support all kinds of external participation. For example, CSV,YML,JSON files and even the return value of methods can also be used as input parameters. You only need to implement the ArgumentsProvider interface, and any external file can be used as its input parameter.
Using @ ValueSource and other annotations to specify input parameters, we can use different parameters to conduct multiple unit tests without adding a unit test every time a parameter is added, which saves a lot of redundant code.
@ValueSource: Specifies the input parameter source for parametric testing. It supports eight basic classes, String type and Class type
@NullSource: indicates that a null input parameter is provided for the parameterized test
@EnumSource: indicates that an enumeration input parameter is provided for the parameterized test
@CsvFileSource: means to read the contents of the specified CSV file as the parameterized test input parameter
@MethodSource: means to read the return value of the specified method as the parameterized test input parameter (note that the method return needs to be a stream)
@ParameterizedTest @ValueSource(strings = {"one", "two", "three"}) @DisplayName("Parametric test 1") public void parameterizedTest1(String string) { System.out.println(string); Assertions.assertTrue(StringUtils.isNotBlank(string)); } @ParameterizedTest @MethodSource("method") //Specify method name @DisplayName("Method source parameters") public void testWithExplicitLocalMethodSource(String name) { System.out.println(name); Assertions.assertNotNull(name); } static Stream<String> method() { return Stream.of("apple", "banana"); }
7, Migration guide
The following changes should be noted during migration:
- Annotations at org junit. jupiter. In the API package, the assertions are in org junit. jupiter. api. In the assertions class, the preconditions are in org junit. jupiter. api. In the assumptions class.
- Replace @ Before and @ After with @ BeforeEach and @ AfterEach.
- Replace @ BeforeClass and @ AfterClass with @ BeforeAll and @ AfterAll.
- Replace @ Ignore with @ Disabled.
- Replace @ Category with @ Tag.
- Replace @ RunWith, @ Rule and @ ClassRule with @ ExtendWith.