《Java单元测试实战》——编写方法:Java编程技巧之单元测试用例编写流程(2)

简介: 《Java单元测试实战》——编写方法:Java编程技巧之单元测试用例编写流程(2)

《Java单元测试实战》——编写方法:Java编程技巧之单元测试用例编写流程(1) https://developer.aliyun.com/article/1232420?groupCode=java



3) 一个有依赖的单元测试

 

一个有依赖的单元测试,需要四大步骤:

 

定义对象:定义测试对象、模拟依赖对象、注入依赖对象;

模拟方法:模拟参数或返回值、模拟依赖方法;

调用方法:传入参数对象、调用测试方法、验证返回值或异常;

验证方法:验证依赖方法、验证方法参数、验证依赖对象。

 

案例代码


/**
 * 用户服务类
 */
@Service
public class UserService {
    /** 定义依赖对象 */
    /** 用户DAO */
    @Autowired
    private UserDAO userDAO;
    /** 标识生成器 */
    @Autowired
    private IdGenerator idGenerator;
    /** 定义依赖参数 */
    /** 可以修改 */
    @Value("${userService.canModify}")
    private Boolean canModify;
    /**
     * 保存用户
     * 
     * @param userSave 用户保存
     * @return 用户标识
     */
    public Long saveUser(UserVO userSave) {
        // 获取用户标识
        Long userId = userDAO.getIdByName(userSave.getName());
        // 根据存在处理
        // 根据存在处理: 不存在则创建
        if (Objects.isNull(userId)) {
            userId = idGenerator.next();
            UserDO userCreate = new UserDO();
            userCreate.setId(userId);
            userCreate.setName(userSave.getName());
            userCreate.setDescription(userSave.getDescription());
            userDAO.create(userCreate);
        }
        // 根据存在处理: 已存在可修改
        else if (Boolean.TRUE.equals(canModify)) {
            UserDO userModify = new UserDO();
            userModify.setId(userId);
            userModify.setName(userSave.getName());
            userModify.setDescription(userSave.getDescription());
            userDAO.modify(userModify);
        }
        // 根据存在处理: 已存在禁修改
        else {
            throw new UnsupportedOperationException("不支持修改");
        }
        // 返回用户标识
        return userId;
    }
}

测试用例

 

/**
 * 用户服务测试类
 */
@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
    /** 定义静态常量 */
    /** 资源路径 */
    private static final String RESOURCE_PATH = "testUserService/";
    /** 模拟依赖对象 */
    /** 用户DAO */
    @Mock
    private UserDAO userDAO;
    /** 标识生成器 */
    @Mock
    private IdGenerator idGenerator;
    /** 定义测试对象 */
    /** 用户服务 */
    @InjectMocks
    private UserService userService;
    /**
     * 在测试之前
     */
    @Before
    public void beforeTest() {
        Whitebox.setInternalState(userService, "canModify", Boolean.TRUE);
    }
    /**
     * 测试: 保存用户-创建
     */
    @Test
    public void testSaveUserWithCreate() {
        // 模拟依赖方法
        // 模拟依赖方法: userDAO.getIdByName
        Mockito.doReturn(null).when(userDAO).getIdByName(Mockito.anyString());
        // 模拟依赖方法: idGenerator.next
        Long userId = 123L;
        Mockito.doReturn(userId).when(idGenerator).next();
        // 调用测试方法
        String path = RESOURCE_PATH + "testSaveUserWithCreate/";
        String text = ResourceHelper.getResourceAsString(getClass(), path + "userSave.json");
        UserSaveVO userSave = JSON.parseObject(text, UserSaveVO.class);
        Assert.assertEquals("用户标识不一致", userId, userService.saveUser(userSave));
        // 验证依赖方法
        // 验证依赖方法: userDAO.getIdByName
        Mockito.verify(userDAO).getIdByName(userSave.getName());
        // 验证依赖方法: idGenerator.next
        Mockito.verify(idGenerator).next();
        // 验证依赖方法: userDAO.create
        ArgumentCaptor<UserDO> userCreateCaptor = ArgumentCaptor.forClass(UserDO.class);
        Mockito.verify(userDAO).create(userCreateCaptor.capture());
        text = ResourceHelper.getResourceAsString(getClass(), path + "userCreate.json");
        Assert.assertEquals("用户创建不一致", text, JSON.toJSONString(userCreateCaptor.getValue()));
        // 验证依赖对象
        Mockito.verifyNoMoreInteractions(userDAO, idGenerator);
    }
    /**
     * 测试: 保存用户-修改
     */
    @Test
    public void testSaveUserWithModify() {
        // 模拟依赖方法
        // 模拟依赖方法: userDAO.getIdByName
        Long userId = 123L;
        Mockito.doReturn(userId).when(userDAO).getIdByName(Mockito.anyString());
        // 调用测试方法
        String path = RESOURCE_PATH + "testSaveUserWithModify/";
        String text = ResourceHelper.getResourceAsString(getClass(), path + "userSave.json");
        UserSaveVO userSave = JSON.parseObject(text, UserSaveVO.class);
        Assert.assertEquals("用户标识不一致", userId, userService.saveUser(userSave));
        // 验证依赖方法
        // 验证依赖方法: userDAO.getIdByName
        Mockito.verify(userDAO).getIdByName(userSave.getName());
        // 验证依赖方法: userDAO.modify
        ArgumentCaptor<UserDO> userModifyCaptor = ArgumentCaptor.forClass(UserDO.class);
        Mockito.verify(userDAO).modify(userModifyCaptor.capture());
        text = ResourceHelper.getResourceAsString(getClass(), path + "userModify.json");
        Assert.assertEquals("用户修改不一致", text, JSON.toJSONString(userModifyCaptor.getValue()));
        // 验证依赖对象
        Mockito.verifyNoMoreInteractions(userDAO, idGenerator);
    }
    /**
     * 测试: 保存用户-异常
     */
    @Test
    public void testSaveUserWithException() {
        // 注入依赖对象
        Whitebox.setInternalState(userService, "canModify", Boolean.FALSE);
        // 模拟依赖方法
        // 模拟依赖方法: userDAO.getIdByName
        Mockito.doReturn(123L).when(userDAO).getIdByName(Mockito.anyString());
        // 调用测试方法
        String path = RESOURCE_PATH + "testSaveUserWithException/";
        String text = ResourceHelper.getResourceAsString(getClass(), path + "userSave.json");
        UserSaveVO userSave = JSON.parseObject(text, UserSaveVO.class);
        UnsupportedOperationException exception = Assert.assertThrows("异常类型不一致", UnsupportedOperationException.class,
            () -> userService.saveUser(userSave));
        Assert.assertEquals("异常消息不一致", "不支持修改", exception.getMessage());
        // 验证依赖方法
        // 验证依赖方法: userDAO.getIdByName
        Mockito.verify(userDAO).getIdByName(userSave.getName());
        // 验证依赖对象
        Mockito.verifyNoMoreInteractions(userDAO, idGenerator);
    }
}
其中,加载的JSON资源文件内容如下:
userSave.json:
userCreate.json:
userModify.json:

通过执行以上测试用例,可以看到对源代码进行了100%的行覆盖。




《Java单元测试实战》——编写方法:Java编程技巧之单元测试用例编写流程(3) https://developer.aliyun.com/article/1232418?groupCode=java

 

相关文章
|
Java 关系型数据库 数据库
Java 项目实战教程从基础到进阶实战案例分析详解
本文介绍了多个Java项目实战案例,涵盖企业级管理系统、电商平台、在线书店及新手小项目,结合Spring Boot、Spring Cloud、MyBatis等主流技术,通过实际应用场景帮助开发者掌握Java项目开发的核心技能,适合从基础到进阶的学习与实践。
1651 4
|
缓存 前端开发 Java
基于最新 Java 技术栈的在线任务管理系统开发实战详解
本项目基于最新Java技术栈开发在线任务管理系统,涵盖任务创建、分配、跟踪、统计等功能。采用Spring Boot 3.2.x、React 18、PostgreSQL 16等主流技术,详解项目架构设计、核心功能实现及部署流程,助力掌握现代Java全栈开发技能。
635 6
|
前端开发 Java API
2025 年 Java 全栈从环境搭建到项目上线实操全流程指南:Java 全栈最新实操指南(2025 版)
本指南涵盖2025年Java全栈开发核心技术,从JDK 21环境搭建、Spring Boot 3.3实战、React前端集成到Docker容器化部署,结合最新特性与实操流程,助力构建高效企业级应用。
3353 1
|
Java API Maven
2025 Java 零基础到实战最新技术实操全攻略与学习指南
本教程涵盖Java从零基础到实战的全流程,基于2025年最新技术栈,包括JDK 21、IntelliJ IDEA 2025.1、Spring Boot 3.x、Maven 4及Docker容器化部署,帮助开发者快速掌握现代Java开发技能。
1924 1
|
11月前
|
安全 Java 开发者
告别NullPointerException:Java Optional实战指南
告别NullPointerException:Java Optional实战指南
424 119
|
存储 前端开发 Java
【JAVA】Java 项目实战之 Java Web 在线商城项目开发实战指南
本文介绍基于Java Web的在线商城技术方案与实现,涵盖三层架构设计、MySQL数据库建模及核心功能开发。通过Spring MVC + MyBatis + Thymeleaf实现商品展示、购物车等模块,提供完整代码示例,助力掌握Java Web项目实战技能。(238字)
1340 0
|
Java 开发者
Java并发编程:CountDownLatch实战解析
Java并发编程:CountDownLatch实战解析
675 100
|
数据采集 JSON Java
Java爬虫获取1688店铺所有商品接口数据实战指南
本文介绍如何使用Java爬虫技术高效获取1688店铺商品信息,涵盖环境搭建、API调用、签名生成及数据抓取全流程,并附完整代码示例,助力市场分析与选品决策。
|
消息中间件 Java 数据库
Java 基于 DDD 分层架构实战从基础到精通最新实操全流程指南
本文详解基于Java的领域驱动设计(DDD)分层架构实战,结合Spring Boot 3.x、Spring Data JPA 3.x等最新技术栈,通过电商订单系统案例展示如何构建清晰、可维护的微服务架构。内容涵盖项目结构设计、各层实现细节及关键技术点,助力开发者掌握DDD在复杂业务系统中的应用。
2151 0
|
11月前
|
Java
Java语言实现字母大小写转换的方法
Java提供了多种灵活的方法来处理字符串中的字母大小写转换。根据具体需求,可以选择适合的方法来实现。在大多数情况下,使用 String类或 Character类的方法已经足够。但是,在需要更复杂的逻辑或处理非常规字符集时,可以通过字符流或手动遍历字符串来实现更精细的控制。
620 18