单元测试之Mockito

依赖

引入 Mockito 和 JUnit 依赖:

<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
<!--
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.6.1</version>
<scope>test</scope>
</dependency>
-->
<!-- 如果需要对静态方法进行打桩,使用 mockito-inline 代替 mockito-core -->
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-inline -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>4.6.1</version>
<scope>test</scope>
</dependency>
<!-- 引入 MockitoExtension 类 -->
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-junit-jupiter -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>4.6.1</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.9.0-M1</version>
<scope>test</scope>
</dependency>

IDEA

快捷键:

  • Ctrl + Shift + T 生成/查找测试类
  • Alt + Insert -> test 生成测试类

查看测试覆盖率:

JUnit

JUnit 注解:

class StudentServiceTest {
@BeforeAll
public static void beforeAll() {
// 所有方法之前执行,只执行一次
}
@AfterAll
public static void afterAll() {
// 所有方法之后执行,只执行一次
}
@BeforeEach
public void beforeEach() {
// 每个方法之前执行
}
@AfterEach
public void afterEach() {
// 每个方法之后执行
}
@Test
void getById() {
// 测试方法
}
}

Mockito

Mockito 注解:

  • @ExtendWith(MockitoExtension.class):使 Mockito 注解生效
  • @InjectMocks:用于标记测试类,它会将 Mock 对象和 Spy 对象注入测试类
  • @Mock:虚构一个对象,如果没有打桩,该对象的方法会返回类型的默认值
  • @Spy:虚构一个对象,如果没有打桩,该对象的方法会调用真实的方法

打桩:通过 when() 定义方法的行为

不要将 @InjectMocks@Spy 同时使用,会导致不可预测的结果

@ExtendWith(MockitoExtension.class)
class StudentServiceTest {
@InjectMocks
private StudentService studentService;
@Mock
private StudentDao studentDao;
@Spy
private Random random;
@Test
void getById() {
// 测试方法
}
}

单元测试

单元测试时,我们仅需要对测试类的业务逻辑进行测试,而它的依赖(比如数据库、请求对象或其他中间件)可以通过 mock 来虚构并注入。

@Test
void test1() {
// 未对 StudentDao 进行打桩,默认返回 null
Student student = studentService.getById(1L);
assertNull(student);
}
@Test
public void test2() {
// 打桩,返回学生对象
when(studentDao.getById(1L)).thenReturn(new Student(1L, "小明", 18));
Student student = studentService.getById(1L);
assertNotNull(student);
assertEquals(1L, student.getId());
assertEquals("小明", student.getName());
assertEquals(18, student.getAge());
verify(studentDao).getById(1L); // 验证 studentDao.getById(1L) 被调用 1 次
verify(studentDao, times(1)).getById(1L); // 同上,但可以指定调用次数
}
@Test
public void test3() {
// 打桩,抛出异常
when(studentDao.getById(1L)).thenThrow(new RuntimeException());
assertThrows(RuntimeException.class, () -> studentService.getById(1L));
}
@Test
public void test4() {
// 对静态方法进行打桩
try (MockedStatic<StaticUtils> staticUtils = mockStatic(StaticUtils.class)) {
staticUtils.when(StaticUtils::name).thenReturn("nothing");
assertEquals("nothing", StaticUtils.name());
}
}

参阅

posted @   廖子博  阅读(777)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示