单元测试之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 @ 2022-06-29 22:08  廖子博  阅读(740)  评论(0编辑  收藏  举报