当前位置:首页 > 技术 > 正文内容

使用 Mockito 进行 Java 单元测试

访客 技术 2026年5月27日 3

概述

在分层架构中(如 Controller - Service - Mapper),对 Service 层进行单元测试时,通常需要隔离外部依赖,例如数据库。本文介绍如何通过 Mockito 模拟 DAO 层接口,从而实现不连接真实数据库的快速单元测试。

项目结构与依赖

基于 Spring Boot 的标准工程:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

基础模型类

import lombok.Data;

@Data
public class User {
    private Long id;
    private String username;
    private Integer userAge;
}

数据访问层示例

import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

public interface UserMapper {

    @Select("SELECT id, username, user_age FROM users WHERE id = #{id}")
    User findUserById(@Param("id") Long id);
}

业务逻辑层

import org.springframework.stereotype.Service;
import javax.annotation.Resource;

@Service
public class UserService {

    @Resource
    private UserMapper userDao;

    public boolean checkAdultStatus(Long userId) {
        User userInfo = userDao.findUserById(userId);
        if (userInfo == null) {
            throw new IllegalArgumentException("未找到该用户");
        }
        return userInfo.getUserAge() >= 18;
    }
}

纯 Mockito 单元测试方式

推荐做法:不启动 Spring 上下文,仅使用 Mockito 提供的能力完成测试。

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

@ExtendWith(MockitoExtension.class)
class UserServiceUnitTest {

    @Mock
    private UserMapper userDaoMock;

    @InjectMocks
    private UserService targetService;

    @Test
    void shouldReturnTrueWhenUserIsAdult() {
        User testData = new User();
        testData.setId(100L);
        testData.setUsername("john");
        testData.setUserAge(25);

        when(userDaoMock.findUserById(100L)).thenReturn(testData);

        assertTrue(targetService.checkAdultStatus(100L));
        verify(userDaoMock, times(1)).findUserById(100L);
    }

    @Test
    void shouldReturnFalseForMinorUser() {
        User minor = new User();
        minor.setId(101L);
        minor.setUserAge(16);

        when(userDaoMock.findUserById(101L)).thenReturn(minor);

        assertFalse(targetService.checkAdultStatus(101L));
    }

    @Test
    void shouldThrowExceptionIfUserNotFound() {
        when(userDaoMock.findUserById(anyLong())).thenReturn(null);

        Exception thrown = assertThrows(IllegalArgumentException.class, () -> {
            targetService.checkAdultStatus(999L);
        });

        assertEquals("未找到该用户", thrown.getMessage());
    }
}

结合 Spring 上下文的测试方式

如果确实需要加载部分 Spring 配置,则可以采用如下形式:

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.junit.jupiter.api.Test;
import javax.annotation.Resource;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
class UserServiceIntegrationTest {

    @MockBean
    private UserMapper mockedDao;

    @Resource
    private UserService realService;

    @Test
    void testAdultCheckInSpringContext() {
        User sample = new User();
        sample.setUserAge(22);
        when(mockedDao.findUserById(1L)).thenReturn(sample);

        assertTrue(realService.checkAdultStatus(1L));
    }
}

关键知识点总结

  • @Mock: 创建一个模拟对象替代真实的依赖组件
  • @InjectMocks: 将被 @Mock 标记的对象自动装配进目标类实例中
  • when(...).thenReturn(...): 定义某个方法在特定参数下调用时应返回的结果
  • verify(): 验证某方法是否按预期被调用及其次数
  • assertThrows(): 用于断言指定代码块是否会抛出某种类型的异常

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。