Java单元测试浅析(JUnit+Mockito)
字数 3014 2025-08-11 17:40:32
Java单元测试教学文档(JUnit+Mockito)
1. 单元测试基础概念
1.1 测试阶段划分
- 单元测试:针对计算机程序模块进行输出正确性检验工作
- 集成测试:在单元测试基础上,整合各个模块组成子系统进行测试
- 系统测试:将整个交付所涉及的协作内容都纳入其中考虑,包含计算机硬件、软件、接口等
- 验收测试:在交付或发布之前对所做工作进行测试检验
1.2 单元测试特征
- 主要功能是证明编写的代码内容与期望输出一致
- 最小最低级的测试内容,由程序员自身发起,保证程序基本组件正常
- 主张以过程性的方法为测试单位,简单实用高效为目标
- 专注于测试一小块代码,保证基础功能
- 剥离与外部接口、存储之间的依赖,使单元测试可控
- 任何时间任何顺序执行单元测试都需要成功
2. JUnit框架详解
2.1 JUnit简介
- 官网:https://junit.org/
- 特点:
- Java语言特定设计的单元测试框架
- 标准测试框架
- 支持多种IDE(Idea、Eclipse)
- 可通过Maven引入
- 方便编写测试代码和查看结果
2.2 JUnit重要概念
| 名称 | 功能作用 |
|---|---|
| Assert | 断言方法集合 |
| TestCase | 表示一个测试案例 |
| TestSuite | 包含一组TestCase |
| TestResult | 收集测试结果 |
2.3 JUnit规范
- 测试方法必须使用
@Test修饰 - 测试方法必须使用
public void修饰,不能带参数 - 测试代码包结构应与被测试代码保持一致
- 测试方法必须可以独立测试,方法间不能有依赖
- 测试类一般使用
Test作为类名后缀 - 测试方法一般使用
test作为方法名前缀
2.4 断言API
| 断言方法 | 描述 |
|---|---|
assertNull(String message, Object object) |
检查对象是否为空 |
assertNotNull(String message, Object object) |
检查对象是否不为空 |
assertEquals(String message, Object expected, Object actual) |
检查对象值是否相等 |
assertTrue(String message, boolean condition) |
检查条件是否为真 |
assertFalse(String message, boolean condition) |
检查条件是否为假 |
assertSame(String message, Object expected, Object actual) |
检查对象引用是否相等 |
assertNotSame(String message, Object unexpected, Object actual) |
检查对象引用是否不等 |
assertArrayEquals(String message, Object[] expecteds, Object[] actuals) |
检查数组值是否相等 |
assertThat(String reason, T actual, Matcher<? super T> matcher) |
检查对象是否满足给定规则 |
2.5 JUnit常用注解
@Test:定义测试方法@Test(excepted=xx.class):测试方法抛出指定异常时通过@Test(timeout = 毫秒数):测试方法执行时间是否符合预期
@BeforeClass:所有方法执行前执行,static方法,全局只执行一次@AfterClass:所有方法执行后执行,static方法,全局只执行一次@Before:每个测试方法运行前执行一次@After:每个测试方法运行后执行一次@Ignore:忽略修饰的测试方法@RunWith:更改测试执行器
3. JUnit实践应用
3.1 Maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
3.2 Controller层测试
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MainApplication.class)
public class StudentControllerTest {
@Autowired
private WebApplicationContext applicationContext;
private MockMvc mockMvc;
@Before
public void setupMockMvc(){
mockMvc = MockMvcBuilders.webAppContextSetup(applicationContext).build();
}
@Test
public void addStudent() throws Exception{
String json="{\"name\":\"张三\",\"className\":\"三年级一班\",\"age\":\"20\",\"sex\":\"男\"}";
mockMvc.perform(MockMvcRequestBuilders.post("/student/save")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.accept(MediaType.APPLICATION_JSON_UTF8)
.content(json.getBytes()))
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print());
}
}
3.3 Service层测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class StudentServiceTest {
@Autowired
private StudentService studentService;
@Test
public void getOne() throws Exception {
Student stu = studentService.selectByKey(5);
Assert.assertThat(stu.getName(), CoreMatchers.is("张三"));
}
}
3.4 Dao层测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class StudentDaoTest {
@Autowired
private StudentMapper studentMapper;
@Test
@Rollback(value = true)
@Transactional
public void insertOne() throws Exception {
Student student = new Student();
student.setName("李四");
student.setMajor("计算机学院");
student.setAge(25);
student.setSex('男');
int count = studentMapper.insert(student);
Assert.assertEquals(1, count);
}
}
3.5 异常测试
@Test(expected = ArithmeticException.class)
public void computeScoreTest() {
studentService.computeScore();
}
3.6 测试套件
@RunWith(Suite.class)
@Suite.SuiteClasses({
StudentServiceTest.class,
StudentDaoTest.class
})
public class AllTest {}
3.7 测试覆盖率
- 统计意义:
- 发现代码中的盲点
- 提高代码质量
- 提升代码设计能力
- IDEA查看方法:
- 右键Run 'xxx' with Coverage
- 导出结果:勾选Open generated HTML in browser
3.8 JUnit插件自动生成
- 安装插件
- 配置插件
- 在类上右键generate...生成测试代码
4. Mockito框架详解
4.1 Mockito简介
- 官网:https://site.mockito.org
- 特点:
- 可以模拟类不仅仅是接口
- 通过注解方式简单易懂
- 支持顺序验证
- 具备参数匹配器
4.2 Mockito使用场景
- 实际对象很难被构造出来
- 实际对象的特定行为很难被触发
- 实际对象可能当前还不存在
4.3 Mockito实践案例
@Test
public void orderBookTest() {
Book expectBook = new Book(1L, "钢铁是怎样炼成的", "书架A01");
Mockito.when(bookService.orderBook(any(String.class))).thenReturn(expectBook);
Book book = studentService.orderBook("");
System.out.println(book);
Assert.assertTrue("预定书籍不符", expectBook.equals(book));
}
4.4 Mockito常用API
mock:模拟一个需要的对象when:配合thenXXX使用,定义当执行什么操作后怎样any:返回特定对象的缺省值thenReturn:执行特定操作后返回指定结果spy:创建监控对象verify:验证特定行为doReturn:返回结果doThrow:抛出特定异常doAnswer:自定义响应times:操作执行次数atLeastOnce:操作至少执行一次atLeast:操作至少执行指定次数atMost:操作至多执行指定次数atMostOnce:操作至多执行一次doNothing:不做任何处理doCallRealMethod:监控对象返回真实结果
4.5 Mockito使用要点
-
打桩(Stub):
Mockito.when(bookService.orderBook(any(String.class))).thenReturn(expectBook); -
参数匹配:
when(mockedList.get(anyInt())).thenReturn(1000); -
次数验证:
verify(mockedList, times(3)).get(1); verify(mockedList, atLeastOnce()).get(1); verify(mockedList, atLeast(3)).get(1); -
顺序验证:
when(bookService.orderBook("")) .thenReturn(JSON.parseObject(json1, Book.class)) .thenReturn(JSON.parseObject(json2, Book.class)) .thenReturn(JSON.parseObject(json3, Book.class)); -
异常验证:
@Test(expected = RuntimeException.class) public void exceptionTest() { List mockedList = mock(List.class); doThrow(new RuntimeException()).when(mockedList).add(1); mockedList.add(1); }
5. 最佳实践总结
-
单元测试原则:
- 保持测试独立性和可重复性
- 测试代码应与生产代码同等重视
- 测试应快速执行
- 测试应覆盖主要业务逻辑
-
JUnit与Mockito结合:
- 使用JUnit进行基本测试框架
- 使用Mockito模拟外部依赖
- 结合Spring Test进行集成测试
-
持续改进:
- 定期检查测试覆盖率
- 重构测试代码保持可维护性
- 将测试纳入持续集成流程