单元测试(Unit Testing)测试代码中的最小单元(比如一个函数或类,不包括接口),确保单个功能模块在孤立的情况下运行正常。
特点:不依赖外部系统(如数据库,API)
每写完一个类或接口,就应该立即为它编写单元测试,随着项目功能的扩展,如果修改了已有类,需要补充或更新相关的单元测试。
单元测试最优实践:
- 优先测试Service层和核心逻辑(如业务规则、工具类)
- 使用Mock框架(如Mockito)隔离外部依赖
假设现在需要对StudentServiceImpl这个类写单元测试,我们使用JUnit和Mockito
@Service
public class StudentServiceImpl implements StudentService{
@Autowired
private StudentRepository studentRepository;
@Override
public Student saveStudent(Student student) {
return studentRepository.save(student);
}
@Override
public List<Student> saveAllStudents(List<Student> studentList) {
return studentRepository.saveAll(studentList);
}
@Override
public void deleteById(Long id) {
studentRepository.deleteById(id);
}
@Override
public List<Student> getStudentByAgeGreaterThan(Integer age) {
return studentRepository.findByAgeGreaterThan(age);
}
@Override
public List<Student> searchStudentsByName(String namePart) {
return studentRepository.findStudentsByNameContaining(namePart);
}
@Override
public Long countStudentsInClass(Long classId) {
return studentRepository.countStudentsByClassId(classId);
}
@Override
public List<Student> getStudentsByClassId(Long classId) {
return studentRepository.findStudentsByClassId(classId);
}
}
步骤1:引入必要的依赖
以下都是非必需,选择添加的依赖
<!-- Junit 5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
</dependency>
<!-- Mockito 核心 -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</dependency>
<!-- Mockito 对 static 支持 -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
</dependency>
<!-- Spring 对单元测试支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
- junit5:必须的组件,提供了最重要的注解和单元测试能力
- mockito-core:必须的组件,提供了最重要的 Mock 的能力
- mockito-inline:非必须组件,提供了对静态方法的 Mock 能力,如果不需要对静态方法进行 Mock 则可以不需要
- spring-boot-starter-test:非必须组件,提供了对类的 private 变量的赋值能力,实际上反射也可以做到,但是通常为了方便,可以直接使用已经有的轮子
步骤2:编写测试类
public class StudentServiceImplTest {
@InjectMocks //创建并注入被测试类的依赖
private StudentServiceImpl studentService;
@Mock //避免与实际数据库交互
private StudentRepository studentRepository;
@BeforeEach
void setUp(){
MockitoAnnotations.openMocks(this); //初始化上面俩注解
}
@Test
void testSaveStudent(){
Student student = new Student(1L,"Tom",20,33L);
when(studentRepository.save(student)).thenReturn(student);
Student result = studentService.saveStudent(student);
assertNotNull(result);
assertEquals("Tom",result.getName());
verify(studentRepository,times(1)).save(student);
}
@Test
void testSaveAllStudents(){
List<Student> students = Arrays.asList(
new Student(1L,"Tom",18,23L),
new Student(2L,"Jerry",19,33L)
);
when(studentRepository.saveAll(students)).thenReturn(students);
List<Student> result = studentService.saveAllStudents(students);
assertNotNull(result);
assertEquals(2,result.size());
verify(studentRepository,times(1)).saveAll(students);
}
@Test
void testDeleteById(){
studentService.deleteById(1L);
verify(studentRepository,times(1)).deleteById(1L);
}
@Test
void testGetStudentByAgeGreaterThan() {
List<Student> students = Arrays.asList(
new Student(1L, "Charlie", 22,10L),
new Student(2L, "Dave", 25,23L)
);
when(studentRepository.findByAgeGreaterThan(20)).thenReturn(students);
List<Student> result = studentService.getStudentByAgeGreaterThan(20);
assertNotNull(result);
assertEquals(2, result.size());
verify(studentRepository, times(1)).findByAgeGreaterThan(20);
}
@Test
void testSearchStudentsByName() {
List<Student> students = Arrays.asList(
new Student(1L, "Alice Smith", 18,23L),
new Student(2L, "Alice Johnson", 19,12L)
);
when(studentRepository.findStudentsByNameContaining("Alice")).thenReturn(students);
List<Student> result = studentService.searchStudentsByName("Alice");
assertNotNull(result);
assertEquals(2, result.size());
verify(studentRepository, times(1)).findStudentsByNameContaining("Alice");
}
@Test
void testCountStudentsInClass() {
when(studentRepository.countStudentsByClassId(1L)).thenReturn(5L);
Long count = studentService.countStudentsInClass(1L);
assertNotNull(count);
assertEquals(5L, count);
verify(studentRepository, times(1)).countStudentsByClassId(1L);
}
@Test
void testGetStudentsByClassId() {
List<Student> students = Arrays.asList(
new Student(1L, "Fiona", 20,10L),
new Student(2L, "Frank", 21,23L)
);
when(studentRepository.findStudentsByClassId(1L)).thenReturn(students);
List<Student> result = studentService.getStudentsByClassId(1L);
assertNotNull(result);
assertEquals(2, result.size());
verify(studentRepository, times(1)).findStudentsByClassId(1L);
}
}
参考资料: