mockito @方法上的可缓存测试

k10s72fa  于 2022-11-08  发布在  其他
关注(0)|答案(1)|浏览(210)

我在一个类中有一个@Cacheable方法。我尝试在第一次调用该方法后创建缓存,然后,第二次调用不应该进入方法getCacheLeads

@Service
public class LeadService {

    @Autowired
    private LeadRepository leadRepository;

    @Autowired
    public LeadService(LeadRepository leadRepository) {
        this.leadRepository = leadRepository;
    }

    public void calculateLead(Lead leadBean) {
        Lead lead = this.getCacheLeads(leadBean);
    }

    @Cacheable(cacheNames="leads", key="#leadBean.leadId")
    public Lead getCacheLeads(Lead leadBean){
        Lead result = leadRepository.findByLeadId(leadBean.getLeadId());
      ***logic to transform de Lead object***
        return result;
    }
}

但在测试期间,该缓存从未被使用,使用相同的参数(serviceIsCalled)调用它两次,以确保它被调用两次来检查它。

@ExtendWith(SpringExtension.class)
public class LeadServiceTest {

    private LeadService leadService;

    @Mock
    private LeadRepository leadRepository;

    @Autowired 
    CacheManager cacheManager;

    @BeforeEach
    public void setUp(){
        leadService = new LeadService(leadRepository);
    }

    @Configuration
    @EnableCaching
    static class Config {
        @Bean
        CacheManager cacheManager() {
            return new ConcurrentMapCacheManager("leads"); 
        }
    }

    @Test
    public void testLead(){
        givenData();
        serviceIsCalled();
        serviceIsCalled();
        checkDataArray();
    }

    private void givenData() {
        Lead lead = new Lead();
        lead.setLeadId("DC635EA19A39EA128764BB99052E5D1A9A");

        Mockito.when(leadRepository.findByLeadId(any()))
        .thenReturn(lead);
    }

    private void serviceIsCalled(){
        Lead lead = new Lead();
        lead.setLeadId("DC635EA19A39EA128764BB99052E5D1A9A");
        leadService.calculateLead(lead);
    }

    private void checkDataArray(){
        verify(leadRepository, times(1)).findByLeadId(anyString());
    }
}

为什么叫了两次?

tjvv9vkg

tjvv9vkg1#

你在这里有很多事情要做,有人看着这个,回答你的问题,肯定会读到字里行间的意思。
首先,您的Spring配置甚至不正确,您正在使用ConcurrentMapCacheManager构造函数“静态”声明Spring应用程序(和测试)使用的所有缓存的名称,该构造函数接受缓存名称数组作为参数。
注意:由名称明确标识的高速缓存,并且只有这些高速缓存在运行时可用。

@Bean
CacheManager cacheManager() {
    return new ConcurrentMapCacheManager("LEAD_DATA"); 
}

在这种情况下,您的唯一缓存称为“LEAD_DATA”。
注意:只有'ConcurrentMapCacheManager中的no arg构造函数允许在运行时按名称动态创建缓存。
但是,在您的@ServiceLeadService@CacheablegetCacheLeads(:Lead)方法中,您声明了要用作“leads”的缓存。

@Service
public class LeadService {

    @Cacheable(cacheNames="leads", key="#leadBean.leadId")
    public Lead getCacheLeads(Lead leadBean){
        // ...
    }
}

这种配置缺失实际上会在运行时导致类似于以下内容的异常:

java.lang.IllegalArgumentException: Cannot find cache named 'leads' for Builder[public io.stackoverflow.questions.spring.cache.StaticCacheNamesIntegrationTests$Lead io.stackoverflow.questions.spring.cache.StaticCacheNamesIntegrationTests$LeadService.load(io.stackoverflow.questions.spring.cache.StaticCacheNamesIntegrationTests$Lead)] caches=[leads] | key='#lead.id' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'

    at org.springframework.cache.interceptor.AbstractCacheResolver.resolveCaches(AbstractCacheResolver.java:92)
    at org.springframework.cache.interceptor.CacheAspectSupport.getCaches(CacheAspectSupport.java:252)
    at org.springframework.cache.interceptor.CacheAspectSupport$CacheOperationContext.<init>(CacheAspectSupport.java:724)
    at org.springframework.cache.interceptor.CacheAspectSupport.getOperationContext(CacheAspectSupport.java:265)
    at org.springframework.cache.interceptor.CacheAspectSupport$CacheOperationContexts.<init>(CacheAspectSupport.java:615)
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:345)
    at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:64)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698)
    at io.stackoverflow.questions.spring.cache.StaticCacheNamesIntegrationTests$LeadService$$EnhancerBySpringCGLIB$$86664246.load(<generated>)
...
..
.

此外,我没有看到LeadsService bean“外部”调用@CacheablegetCacheLeads(..)方法的任何内容。

leadService.calculateLead(lead);

具体如下:

private void serviceIsCalled(){
    Lead lead = new Lead();
    lead.setLeadId("DC635EA19A39EA128764BB99052E5D1A9A");
    leadService.calculateLead(lead);
}

如果calculateLead(:Lead)LeadService方法正在调用@CacheablegetCacheLeads(:Lead)LeadService方法(内部),那么这将不会导致缓存功能启动,因为您已经“落后于”Spring设置的AOP代理,以便为您的LeadService bean“启用”缓存行为。
请参见Spring框架AOP文档中有关这方面的内容。
注意:Spring的Cache Abstraction,就像Spring的Transaction Management一样,是建立在Spring AOP基础结构之上的,Spring中的许多其他东西也是如此。
在您的情况下,这意味着:

Test -> <PROXY> -> LeadService.calculateLead(:Lead) -> LeadService.getCacheLeads(:Lead)

但是,在LeadSevice.calculateLead(:Lead)LeadService.getCacheLeads(:Lead)之间,不涉及PROXY,因此不会应用Spring的缓存行为。
只是......

Test (or some other bean) -> <PROXY> -> LeadService.getCacheLeads(:Lead)

将导致AOP代理使用被调用的缓存拦截器进行修饰,并应用缓存行为。
您可以看到,如果按照我的example test class(根据您的域建模)中所演示的那样正确配置和使用,您的用例将正常工作。
查找说明您的配置在您的情况下失败的原因的注解。

相关问题