0

0

Mockito使用中变量值未被覆盖问题排查与解决

花韻仙語

花韻仙語

发布时间:2025-10-12 13:02:02

|

689人浏览过

|

来源于php中文网

原创

mockito使用中变量值未被覆盖问题排查与解决

本文旨在帮助开发者理解并解决在使用Mockito进行单元测试时,遇到的变量值未被Mock覆盖的问题。我们将通过分析问题代码、提供示例,并深入探讨Mockito的工作原理,帮助读者掌握正确的Mock使用方法,编写出更可靠的单元测试。

在单元测试中,Mockito是一个强大的Mocking框架,允许我们模拟依赖项的行为,从而隔离被测代码。然而,不当的使用会导致Mock失效,变量值未被覆盖,从而导致测试结果不符合预期。本文将深入探讨这个问题,并提供解决方案。

理解Mockito的工作原理

Mockito通过动态代理字节码操作来创建Mock对象。当我们使用when(...).thenReturn(...)或doReturn(...).when(...)等方法时,实际上是在定义Mock对象的行为,即当调用特定方法时,Mock对象应该返回什么值。

关键在于,Mockito只能Mock接口或类的publicprotected方法。对于private方法或final方法,Mockito无法直接Mock。此外,Mockito的行为定义是针对Mock对象本身的,而不是针对真实对象。

分析问题代码

原始问题中,开发者尝试Mock userRepository.save(userEntityTo) 方法,使其返回一个userEntityTo 对象,该对象的followedByEntity 属性为null,从而触发FollowerNotFoundException。然而,测试结果显示followedByEntity 属性始终不为null,导致测试失败。

问题代码的关键部分如下:

userEntityTo = userRepository.save(userEntityTo);

if (userEntityTo.getFollowedByEntity() == null || userEntityTo.getFollowedByEntity().isEmpty()) {
    throw new FollowerNotFoundException("Follower Not Found");
}

以及测试代码:

doReturn(userEntityTo).when(userRepository).save(any());
userEntityTo.setFollowerOfEntity(null);
userEntityTo.setFollowedByEntity(null);

问题在于,虽然测试代码中设置了userEntityTo.setFollowedByEntity(null),但这只是修改了测试代码中的userEntityTo对象,而userRepository.save(userEntityTo) 方法返回的仍然是原始的、followedByEntity 不为null的userEntityTo对象

解决方案

解决此问题的关键在于确保Mock对象返回的是期望的值。以下是几种可能的解决方案:

  1. 正确设置Mock对象的返回值:

    确保userRepository.save(userEntityTo) 方法返回的是一个followedByEntity 为null的UserEntity 对象。可以使用以下代码:

    UserEntity userEntityToWithNullFollowers = getUserEntity(); // Create a new UserEntity
    userEntityToWithNullFollowers.setId(userToId);
    userEntityToWithNullFollowers.setName("new name");
    userEntityToWithNullFollowers.setFollowedByEntity(null); // Set followedByEntity to null
    
    when(userRepository.save(any())).thenReturn(userEntityToWithNullFollowers);

    这样,userRepository.save(userEntityTo) 方法将返回一个followedByEntity 为null的UserEntity 对象,从而触发异常。

    Synthesys
    Synthesys

    Synthesys是一家领先的AI虚拟媒体平台,用户只需点击几下鼠标就可以制作专业的AI画外音和AI视频

    下载
  2. 检查业务逻辑:

    仔细检查followUser 方法的业务逻辑。如答案中提到,followingRequestEntities 始终包含一个元素,因此userEntityTo.getFollowedByEntity().isEmpty() 永远不会为true。这意味着即使followedByEntity 为null,异常也不会被抛出。需要重新审视业务逻辑,确保异常条件能够被满足。

  3. 使用Spy进行部分Mock:

    如果只需要Mock save 方法,可以使用@Spy 注解来创建一个UserServiceImpl 的Spy对象。Spy对象会调用真实方法,除非显式地Mock了某个方法。

    @Spy
    @InjectMocks
    UserServiceImpl userService;
    
    @Test
    void testFollowUser_ThrowsExceptionWhenFollowerIsFound() {
        // ...
        UserEntity userEntityTo = getUserEntity();
        userEntityTo.setId(userToId);
        userEntityTo.setName("new name");
        userEntityTo.setFollowedByEntity(null);
    
        when(userRepository.save(any())).thenReturn(userEntityTo);
    
        FollowerNotFoundException exception =
                assertThrows(FollowerNotFoundException.class, () -> userService.followUser(userFromId, userToId));
    
        assertEquals("Follower Not Found", exception.getMessage());
    }

    需要注意的是,使用Spy时要谨慎,避免过度Mock,导致测试失去意义。

  4. 考虑测试策略:

    在某些情况下,直接测试if 语句可能不是最佳策略。可以考虑测试userRepository.save() 方法是否被调用,以及是否使用了正确的参数。

代码示例

以下是一个完整的示例,展示了如何使用Mockito正确Mock userRepository.save() 方法:

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 java.util.Optional;
import java.util.UUID;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class UserServiceImplTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserServiceImpl userService;

    @Test
    void testFollowUser_ThrowsExceptionWhenFollowerIsNotFound() {
        // Arrange
        UUID userFromId = UUID.randomUUID();
        UUID userToId = UUID.randomUUID();
        UserEntity userEntityFrom = new UserEntity();
        userEntityFrom.setId(userFromId);

        UserEntity userEntityTo = new UserEntity();
        userEntityTo.setId(userToId);
        userEntityTo.setName("new name");

        UserEntity userEntityToWithNullFollowers = new UserEntity();
        userEntityToWithNullFollowers.setId(userToId);
        userEntityToWithNullFollowers.setName("new name");
        userEntityToWithNullFollowers.setFollowedByEntity(null);

        when(userRepository.findById(userFromId)).thenReturn(Optional.of(userEntityFrom));
        when(userRepository.findById(userToId)).thenReturn(Optional.of(userEntityTo));
        when(userRepository.save(any())).thenReturn(userEntityToWithNullFollowers);

        // Act & Assert
        FollowerNotFoundException exception = assertThrows(FollowerNotFoundException.class, () -> userService.followUser(userFromId, userToId));
        assertEquals("Follower Not Found", exception.getMessage());
    }
}

注意事项:

  • 确保Mockito的版本与项目兼容。
  • 避免过度Mock,尽量保持测试的真实性。
  • 仔细阅读Mockito的文档,了解其工作原理和使用方法。
  • 使用debug工具,可以帮助理解代码的执行流程和变量的值。

总结

在使用Mockito进行单元测试时,理解其工作原理至关重要。要确保Mock对象的行为与预期一致,才能编写出可靠的测试。本文通过分析问题代码、提供示例和注意事项,希望能帮助读者解决Mockito使用中遇到的变量值未被覆盖的问题,并编写出更高质量的单元测试。 记住,单元测试的目的是验证代码的正确性,因此要选择合适的测试策略,并保持测试的简洁性和可读性。

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

233

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

437

2024.03.01

if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

760

2023.08.22

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1050

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

106

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

490

2025.12.29

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

11

2026.01.19

Golang 性能分析与pprof调优实战
Golang 性能分析与pprof调优实战

本专题系统讲解 Golang 应用的性能分析与调优方法,重点覆盖 pprof 的使用方式,包括 CPU、内存、阻塞与 goroutine 分析,火焰图解读,常见性能瓶颈定位思路,以及在真实项目中进行针对性优化的实践技巧。通过案例讲解,帮助开发者掌握 用数据驱动的方式持续提升 Go 程序性能与稳定性。

9

2026.01.22

html编辑相关教程合集
html编辑相关教程合集

本专题整合了html编辑相关教程合集,阅读专题下面的文章了解更多详细内容。

53

2026.01.21

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.8万人学习

C# 教程
C# 教程

共94课时 | 7.3万人学习

Java 教程
Java 教程

共578课时 | 49.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号