[TOC]
https://junit.org/junit5/docs/current/user-guide/#running-tests-build-maven
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.7.2</version>
<scope>test</scope>
</dependency>
Spring Boot 2.2.0 版本开始引入 JUnit 5 作为单元测试默认库。
JUnit5 的核心是 JUnit Jupiter。
JUnit5: https://junit.org/junit5/docs/current/user-guide/
需要引入的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
增加测试用例的 IDEA 快捷键:在类中 alt + insert
SpringBoot 2.4 以上版本移除了默认对 Vintage 的依赖。如果需要兼容junit4需要自行引入(不能使用 junit4 的功能 @Test)
<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>
@SpringBootTest + @RunWith(SpringTest.class)
- 与 Maven 的配合,clean - test 会把所有的单元测试跑一遍
- Junit类具有Spring的功能,@Autowired、比如 @Transactional 标注测试方法,测试完成后自动回滚
- 在单元测试中,要使用 Assert 断言机制,不要使用 sout
- 一个业务逻辑开发完后,一定要写单元测试类。
JUnit5 提供的一些注解:
Annotation | Description |
---|---|
@Test |
Denotes that a method is a test method. Unlike JUnit 4’s @Test annotation, this annotation does not declare any attributes, since test extensions in JUnit Jupiter operate based on their own dedicated annotations. Such methods are inherited unless they are overridden. |
@ParameterizedTest |
Denotes that a method is a parameterized test. Such methods are inherited unless they are overridden. |
@RepeatedTest |
Denotes that a method is a test template for a repeated test. Such methods are inherited unless they are overridden. |
@TestFactory |
Denotes that a method is a test factory for dynamic tests. Such methods are inherited unless they are overridden. |
@TestTemplate |
Denotes that a method is a template for test cases designed to be invoked multiple times depending on the number of invocation contexts returned by the registered providers. Such methods are inherited unless they are overridden. |
@TestMethodOrder |
Used to configure the test method execution order for the annotated test class; similar to JUnit 4’s @FixMethodOrder . Such annotations are inherited. |
@TestInstance |
Used to configure the test instance lifecycle for the annotated test class. Such annotations are inherited. |
@DisplayName |
Declares a custom display name for the test class or test method. Such annotations are not inherited. |
@DisplayNameGeneration |
Declares a custom display name generator for the test class. Such annotations are inherited. |
@BeforeEach |
Denotes that the annotated method should be executed before each @Test , @RepeatedTest , @ParameterizedTest , or @TestFactory method in the current class; analogous to JUnit 4’s @Before . Such methods are inherited unless they are overridden. |
@AfterEach |
Denotes that the annotated method should be executed after each @Test , @RepeatedTest , @ParameterizedTest , or @TestFactory method in the current class; analogous to JUnit 4’s @After . Such methods are inherited unless they are overridden. |
@BeforeAll |
Denotes that the annotated method should be executed before all @Test , @RepeatedTest , @ParameterizedTest , and @TestFactory methods in the current class; analogous to JUnit 4’s @BeforeClass . Such methods are inherited (unless they are hidden or overridden) and must be static (unless the "per-class" test instance lifecycle is used). 注意,beforeall 修饰的必须是静态的方法 |
@AfterAll |
Denotes that the annotated method should be executed after all @Test , @RepeatedTest , @ParameterizedTest , and @TestFactory methods in the current class; analogous to JUnit 4’s @AfterClass . Such methods are inherited (unless they are hidden or overridden) and must be static (unless the "per-class" test instance lifecycle is used). |
@Nested |
Denotes that the annotated class is a non-static nested test class. @BeforeAll and @AfterAll methods cannot be used directly in a @Nested test class unless the "per-class" test instance lifecycle is used. Such annotations are not inherited. |
@Tag |
Used to declare tags for filtering tests, either at the class or method level; analogous to test groups in TestNG or Categories in JUnit 4. Such annotations are inherited at the class level but not at the method level. |
@Disabled |
Used to disable a test class or test method; analogous to JUnit 4’s @Ignore . Such annotations are not inherited. |
@Timeout |
Used to fail a test, test factory, test template, or lifecycle method if its execution exceeds a given duration. Such annotations are inherited. |
@ExtendWith |
Used to register extensions declaratively. Such annotations are inherited. |
@RegisterExtension |
Used to register extensions programmatically via fields. Such fields are inherited unless they are shadowed. |
@TempDir |
Used to supply a temporary directory via field injection or parameter injection in a lifecycle method or test method; located in the org.junit.jupiter.api.io package. |
代码Demo:https://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions
注意 @Test 标注,需要使用junit5版本的注解(Jupiter)。
Demo:
@Test
// Transactional 回滚该方法
@Transactional
void update() {
assertNotNull(userService.update());
}
要引入这个静态包
import static java.time.Duration.ofMillis;
import static java.time.Duration.ofMinutes;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTimeout;
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.concurrent.CountDownLatch;
import example.domain.Person;
import example.util.Calculator;
import org.junit.jupiter.api.Test;
class AssertionsDemo {
private final Calculator calculator = new Calculator();
private final Person person = new Person("Jane", "Doe");
@Test
void standardAssertions() {
assertEquals(2, calculator.add(1, 1));
assertEquals(4, calculator.multiply(2, 2),
"The optional failure message is now the last parameter");
assertTrue('a' < 'b', () -> "Assertion messages can be lazily evaluated -- "
+ "to avoid constructing complex messages unnecessarily.");
}
@Test
void groupedAssertions() {
// In a grouped assertion all assertions are executed, and all
// failures will be reported together.
assertAll("person",
() -> assertEquals("Jane", person.getFirstName()),
() -> assertEquals("Doe", person.getLastName())
);
}
@Test
void dependentAssertions() {
// Within a code block, if an assertion fails the
// subsequent code in the same block will be skipped.
assertAll("properties",
() -> {
String firstName = person.getFirstName();
assertNotNull(firstName);
// Executed only if the previous assertion is valid.
assertAll("first name",
() -> assertTrue(firstName.startsWith("J")),
() -> assertTrue(firstName.endsWith("e"))
);
},
() -> {
// Grouped assertion, so processed independently
// of results of first name assertions.
String lastName = person.getLastName();
assertNotNull(lastName);
// Executed only if the previous assertion is valid.
assertAll("last name",
() -> assertTrue(lastName.startsWith("D")),
() -> assertTrue(lastName.endsWith("e"))
);
}
);
}
@Test
void exceptionTesting() {
Exception exception = assertThrows(ArithmeticException.class, () ->
calculator.divide(1, 0));
assertEquals("/ by zero", exception.getMessage());
}
@Test
void timeoutNotExceeded() {
// The following assertion succeeds.
assertTimeout(ofMinutes(2), () -> {
// Perform task that takes less than 2 minutes.
});
}
@Test
void timeoutNotExceededWithResult() {
// The following assertion succeeds, and returns the supplied object.
String actualResult = assertTimeout(ofMinutes(2), () -> {
return "a result";
});
assertEquals("a result", actualResult);
}
@Test
void timeoutNotExceededWithMethod() {
// The following assertion invokes a method reference and returns an object.
String actualGreeting = assertTimeout(ofMinutes(2), AssertionsDemo::greeting);
assertEquals("Hello, World!", actualGreeting);
}
@Test
void timeoutExceeded() {
// The following assertion fails with an error message similar to:
// execution exceeded timeout of 10 ms by 91 ms
assertTimeout(ofMillis(10), () -> {
// Simulate task that takes more than 10 ms.
Thread.sleep(100);
});
}
@Test
void timeoutExceededWithPreemptiveTermination() {
// The following assertion fails with an error message similar to:
// execution timed out after 10 ms
assertTimeoutPreemptively(ofMillis(10), () -> {
// Simulate task that takes more than 10 ms.
new CountDownLatch(1).await();
});
}
private static String greeting() {
return "Hello, World!";
}
}