Mockito 无法创建 @Autowired Spring-Data Repository 的间谍

Posted

技术标签:

【中文标题】Mockito 无法创建 @Autowired Spring-Data Repository 的间谍【英文标题】:Mockito cannot create Spy of @Autowired Spring-Data Repository 【发布时间】:2019-01-16 17:26:03 【问题描述】:

我正在尝试用 Mockito.spy 功能覆盖我的整个测试环境,所以只要我想我可以存根一个方法,但所有其他调用都使用默认功能。 这在 Service 层工作得很好,但我在 Repository 层遇到了问题。

我的设置如下:

Mockito - 2.15.0 春天 - 5.0.8 SpringBoot - 2.0.4

存储库:

public interface ARepository extends CrudRepository<ADBO, Long> 

服务:

@Service
public class AService 

    @Autowired
    ARepository aRepository;

    public ADBO getById(long id) 
        return aRepository.findById(id).orElse(null);
    

    public Iterable<ADBO> getAll() 
        return aRepository.findAll();
    

间谍的配置:

@Profile("enableSpy")
@Configuration
public class SpyConfig 

    @Bean
    @Primary
    public ARepository aRepository() 
        return Mockito.spy(ARepository.class);
    

还有我的测试课:

@ActiveProfiles("enableSpy")
@RunWith(SpringRunner.class)
@SpringBootTest
public class AServiceTest 

    @Autowired
    AService aService;

    @Autowired
    ARepository aRepository;

    @Test
    public void test() 
        ADBO foo = new ADBO();
        foo.setTestValue("bar");
        aRepository.save(foo);

        doReturn(Optional.of(new ADBO())).when(aRepository).findById(1L);
        System.out.println("result (1):" + aService.getById(1));

        System.out.println("result all:" + aService.getAll());

    

现在这个测试有三种可能的结果:

aRepository 既不是模拟也不是间谍:org.mockito.exceptions.misusing.NotAMockException: Argument passed to when() is not a mock! Example of corr...

aRepository 是模拟但不是间谍(这是我得到的结果):result (1):ADBO(id=null, testValue=null) result all:[]

aRepository 是一个间谍(这就是我想要的):result (1):ADBO(id=null, testValue=null) result all:[ADBO(id=1, testValue=bar)]

我将此行为归因于存储库的spring实例化在后台更复杂,并且在调用Mockito.spy(ARepository.class)时未正确实例化存储库。

我还尝试将正确的实例自动装配到配置中,并使用@Autowired 对象调用Mockito.spy()

这会导致:

Cannot mock/spy class com.sun.proxy.$Proxy75
Mockito cannot mock/spy because :
 - final class

根据我的研究,从 v2.0.0 开始,Mockito 可以模拟和监视 final 类。

调用Mockito.mockingDetails(aRepository).isSpy() 返回true,这让我认为后台的对象没有正确实例化。

最后是我的问题:

如何使用 @Autowired 在我的 UnitTest 中获取 Spring-Data Repository 的间谍实例?

【问题讨论】:

为什么这么复杂?使用@SpyBean 注释您要监视的字段,而不是尝试将专用配置类硬塞到位。见docs.spring.io/spring-boot/docs/current/reference/html/… 在 aRepository 上使用 @SpyBean 而不是 @Autowire 会导致相同的行为,它变成模拟但不是间谍。 当docs.spring.io/spring-boot/docs/current/api/org/springframework/… spring boot 可以帮助您时,为什么还要花费如此多的精力和复杂性来围绕存储库进行测试?此外,这还需要您了解存储库的底层复杂性及其工作原理。 有一个打开的bug:github.com/spring-projects/spring-boot/issues/7033 你试过这个吗? ***.com/questions/62698827/… 【参考方案1】:

2021 年 8 月更新

你应该使用@SpyBean

它曾经被破坏(请参阅下面的问题链接),但现在已经修复并支持自 Spring Boot 2.5.3

如果您仍然收到此错误并且无法更新您的 Spring Boot 版本,您可能想尝试@Mateusz Zając 在此问题的答案之一中提出的解决方法。 (如果它适合你,请考虑支持他的答案)

(已关闭)问题的链接: http://github.com/spring-projects/spring-boot/issues/7033

【讨论】:

它也不适用于 Feign 客户端。这是一个已知问题吗? 正如@Mateusz Zając 在评论中指出的那样,Github 问题现已关闭,并且自 Spring Boot 版本 2.5.3 起支持此功能【参考方案2】:

@SpyBean 现在适用于自 Spring Boot 版本 2.5.3

以来的 Spring 数据存储库

如果您无法升级,您可以通过在测试方法主体中实例化您的间谍来解决此问题:

var yourSpy = Mockito.mock(FooRepository.class, AdditionalAnswers.delegatesTo(real))

【讨论】:

以上是关于Mockito 无法创建 @Autowired Spring-Data Repository 的间谍的主要内容,如果未能解决你的问题,请参考以下文章

JUnit 在带有 @Autowired 注释的 Spring Boot 中不起作用

如何使 Spring 的 @Autowired 在 JUnit 5 扩展中工作? [复制]

如何使用 mockito 模拟方法?

“无法解析方法”与 mockito

Mockito mockStatic 无法解析符号

无法初始化插件:接口 org.mockito.plugins.MockMaker