注意:Junit5只有在SpringBoot2.2以上才可以使用
点击进入官网
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
常用注解
- @Test :表示方法是测试方法。但是与JUnit4的@Test不同,他的职责非常单一不能声明任何属性,拓展的测试将会由Jupiter提供额外测试
- @ParameterizedTest :表示方法是参数化测试,下方会有详细介绍
- @DisplayName :为测试类或者测试方法设置展示名称
- @BeforeEach :表示在每个单元测试之前执行
- @AfterEach :表示在每个单元测试之后执行
- @BeforeAll :表示在所有单元测试之前执行//该注解必须注在静态方法上
- @AfterAll :表示在所有单元测试之后执行//该注解必须注在静态方法上
- @Tag :表示单元测试类别,类似于JUnit4中的@Categories,给测试方法打一个标签
- @Disabled :表示测试类或测试方法不执行,类似于JUnit4中的@Ignore
- @Timeout :表示测试方法运行如果超过了指定时间将会返回错误
- @RepeatedTest(int n) :表示测试方法要执行n次,这个注解不要和@Test一块用
@Timeout(value = 500,unit = TimeUnit.MILLISECONDS)
@SpringBootTest
public class JunitTest {
@Autowired
User user;
@BeforeAll
static void testBeforeAll(){
System.out.println("所有测试方法的开始");
}
@AfterAll
static void testAfterAll(){
System.out.println("所有测试方法的结束");
}
@BeforeEach
void testBeforeEach(){
System.out.println("一个测试就要开始了");
}
@AfterEach
void testAfterEach(){
System.out.println("一个测试已经结束了");
}
@Test
@Timeout(value = 500,unit = TimeUnit.MILLISECONDS)
@DisplayName("测试Display方法")
@Tag
public void test(){
System.out.println("测试junit5");
System.out.println(user);
}
@Disabled
@Test
@DisplayName("测试Display方法")
public void test2(){
System.out.println("测试junit5-2");
}
}
断言机制(assertions)
- 断言简介
断言(assertions)是测试方法中的核心部分,用来对测试需要满足的条件进行验证。这些断言方法都是 org.junit.jupiter.api.Assertions 的静态方法。 - 断言作用
检查业务逻辑返回的数据是否合理。
所有的测试运行结束以后,会有一个详细的测试报告; - 断言的机制
我们通过调用Assertions的静态方法,并给它传入相应的参数,断言方法会在判断这传入的参数符合不符合条件,如果不符合条件他就会抛出一个错误
JUnit 5 内置的断言可以分成如下几个类别:
简单断言
对单个值进行验证
@Test
@DisplayName("测试简单断言")
public void test(){
try {
assertEquals(2, 1,"怎么能是不相等的?");
} catch (Exception e) {
e.printStackTrace();
}
// System.out.println("断言成功");
assertSame(127, 127);
assertNotSame(128, 128);
assertTrue(1 == 2,"居然是假的!");
assertFalse(1 > 1);
assertNull(null);
assertNotNull(new String());
}
数组断言
验证数组的里的值是否完全相同,只有一个方法 assertArrayEquals(数组,数组,信息字符串)
@Test
@DisplayName("测试数组断言")
void testArrayAssert(){
int[] array = {1,2};
int[] array2 = {2,1};
assertArrayEquals(array,array2,"数组数据不相同");
}
组合断言
可以组合多个断言
assertAll 方法接受多个 org.junit.jupiter.api.Executable 函数式接口的实例作为要验证的断言,可以通过 lambda 表达式很容易的提供这些断言
@Test
@DisplayName("assert all")
public void all() {
assertAll("Math",
() -> assertEquals(2, 1 + 1),
() -> assertTrue(1 > 0)
);
}
异常断言
顾名思义,我断言下面的代码会出异常,如果没出异常,那我们就抛出error
方法:assertThrows
@Test
@DisplayName("测试异常断言")
void testExceptionAssert(){
assertThrows(Exception.class, ()->{int i = 1/0;},"竟然没有出异常?");
System.out.println("断言成功");
}
超时断言
顾名思义:我断言下面的代码会在多少时间内完成,不完成就抛出error
指定时间使用Duration的ofxxx方法
@Test
@DisplayName("测试超时断言")
void testTimeoutAssert(){
assertTimeout(Duration.ofMillis(1000),()->Thread.sleep(999));
}
快速失败
通过 fail 方法直接使得测试失败
@Test
@DisplayName("测试快速失败")
void testFail(){
fail("我失败了,我不能接受");
}
小知识
写完测试类之后我们可以使用测试组件进行测试,会对所有单元测试进行测试,并且告诉我们出了多少错
//输出的信息
[INFO]
[ERROR] Tests run: 14, Failures: 3, Errors: 1, Skipped: 1
Tests run: 14, Failures: 3, Errors: 1, Skipped: 1
前置条件
JUnit 5 中的前置条件(assumptions【假设】)类似于断言,不同之处在于不满足的断言会使得测试方法失败,而不满足的前置条件只会使得测试方法的执行终止。前置条件可以看成是测试方法执行的前提,当该前提不满足时,就没有继续执行的必要。
@Test
@DisplayName("测试前置条件")
void testAssumptions(){
Assumptions.assumeTrue(true,"他不是正确的!555");
System.out.println("111");
}
在我看来,前置条件类似于给@Disable添加了一个条件
当错误的时候该测试就不往下执行了,就不测试了
嵌套测试
通过@Nested注解来实现,写在内部类上,外部类的测试方法不会调用内部类的BeforeEach方法,内部类里面的测试方法会调用外部类的BeforeEach方法
@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());
}
}
}
}
参数化测试
参数化测试可以使用不同的参数多次运行测试。它们的声明就像常规@Test方法一样,但使用 @ParameterizedTest注释代替。此外,您必须至少声明一个 源,该源将为每次调用提供参数,然后在测试方法中使用这些参数。
- @ValueSource() 也就是和foreach差不多,把数组中的数据遍历一遍进行测试。
@ParameterizedTest
@ValueSource(ints = {1,2,3})
void test(int i){
System.out.println(i);
}
- @ValueSource: 为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型
- @NullSource: 表示为参数化测试提供一个null的入参
- @EnumSource: 表示为参数化测试提供一个枚举入参
- @CsvFileSource:表示读取指定CSV文件内容作为参数化测试入参
- @MethodSource:表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)
Junit4与5的差别
在进行迁移的时候需要注意如下的变化:
- 注解在 org.junit.jupiter.api 包中,断言在 org.junit.jupiter.api.Assertions 类中,前置条件在 org.junit.jupiter.api.Assumptions 类中。
- 把@Before 和@After 替换成@BeforeEach 和@AfterEach。
- 把@BeforeClass 和@AfterClass 替换成@BeforeAll 和@AfterAll。
- 把@Ignore 替换成@Disabled。
- 把@Category 替换成@Tag。
- 把@RunWith、@Rule 和@ClassRule 替换成@ExtendWith。