单元测试-嵌套测试
# 590.单元测试-嵌套测试
嵌套测试可以让测试方法更有层次,一般一个项目是包含多个模块的,而模块下又包含很多功能(层层嵌套),此时嵌套测试就能表现出层级关系。
文档:JUnit 5 User Guide #writing-tests-nested (opens new window)
# 简介
JUnit 5 可以通过 Java 中的内部类和@Nested 注解实现嵌套测试,从而可以更好的把相关的测试方法组织在一起。在内部类中可以使用@BeforeEach 和@AfterEach 注解,而且嵌套的层次没有限制。
嵌套测试要使用注解@Nested,该注解作用在类上,表示类与类之间的嵌套
package com.peterjxl.learnspringbootwebadmin;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.util.EmptyStackException;
import java.util.Stack;
import static org.junit.jupiter.api.Assertions.*;
@DisplayName("嵌套测试")
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());
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
代码分析:一个类中,包含了一个测试方法 isInstantiatedWithNew
,以及一个内部类 WhenNew
@DisplayName("嵌套测试")
class TestingAStackDemo {
Stack<Object> stack;
@Test
@DisplayName("is instantiated with new Stack()")
void isInstantiatedWithNew() {
new Stack<>();
}
@Nested
@DisplayName("when new")
class WhenNew {
//...........
2
3
4
5
6
7
8
9
10
11
12
13
14
15
内部类中包含如下方法:
@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);
}
//....
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
可以看到有一个 @BeforeEach
方法,3 个 @Test
方法。思考一个问题,嵌套测试类中的 @BeforeEach
方法,会不会在外部的测试方法中执行?也就是运行 isInstantiatedWithNew
方法的时候,会不会先执行内部类中的 createNewStack
方法?
我们来测试下:
@Test
@DisplayName("is instantiated with new Stack()")
void isInstantiatedWithNew() {
new Stack<>();
assertNotNull(stack);
}
2
3
4
5
6
运行结果:
org.opentest4j.AssertionFailedError: expected: not <null>
结论:嵌套测试情况下,外层测试类的不能驱动内层测试类的执行(包括 Before、After、Each、All 等注解)
但是内层的测试类,是可以驱动外层的测试方法的。例如在内部类 AfterPushing
中,有个测试方法是 push 元素的:
@BeforeEach
void pushAnElement() {
stack.push(anElement);
}
2
3
4
而想要 push 成功,那么首先 stack 不能为 null,因此也就是会先运行外层的测试方法 createNewStack
接下来的代码都比较简单了,就是测试能否正常 new,new 之后能否正常抛出异常;然后测试 push 一个元素后,能否正常读取。
我们运行整个测试类,可以看到层次还是挺分明的:
# 源码
已将本文源码上传到 Gitee (opens new window) 或 GitHub (opens new window) 的分支 demo20,读者可以通过切换分支来查看本文的示例代码