《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

 

相关文章
|
5月前
|
Java
Java语言实现字母大小写转换的方法
Java提供了多种灵活的方法来处理字符串中的字母大小写转换。根据具体需求,可以选择适合的方法来实现。在大多数情况下,使用 String类或 Character类的方法已经足够。但是,在需要更复杂的逻辑或处理非常规字符集时,可以通过字符流或手动遍历字符串来实现更精细的控制。
398 18
|
5月前
|
Java 编译器 Go
【Java】(5)方法的概念、方法的调用、方法重载、构造方法的创建
Java方法是语句的集合,它们在一起执行一个功能。方法是解决一类问题的步骤的有序组合方法包含于类或对象中方法在程序中被创建,在其他地方被引用方法的优点使程序变得更简短而清晰。有利于程序维护。可以提高程序开发的效率。提高了代码的重用性。方法的名字的第一个单词应以小写字母作为开头,后面的单词则用大写字母开头写,不使用连接符。例如:addPerson。这种就属于驼峰写法下划线可能出现在 JUnit 测试方法名称中用以分隔名称的逻辑组件。
276 4
|
5月前
|
编解码 Java 开发者
Java String类的关键方法总结
以上总结了Java `String` 类最常见和重要功能性方法。每种操作都对应着日常编程任务,并且理解每种操作如何影响及处理 `Strings` 对于任何使用 Java 的开发者来说都至关重要。
371 5
|
6月前
|
算法 安全 Java
除了类,Java中的接口和方法也可以使用泛型吗?
除了类,Java中的接口和方法也可以使用泛型吗?
214 11
|
5月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
281 1
|
5月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
301 1
|
6月前
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
Java 数据库 Spring
265 0
|
6月前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
456 16
|
7月前
|
缓存 并行计算 安全
关于Java多线程详解
本文深入讲解Java多线程编程,涵盖基础概念、线程创建与管理、同步机制、并发工具类、线程池、线程安全集合、实战案例及常见问题解决方案,助你掌握高性能并发编程技巧,应对多线程开发中的挑战。