在 Spring 的构建后阶段之前注入 mock

Posted

技术标签:

【中文标题】在 Spring 的构建后阶段之前注入 mock【英文标题】:Injecting mock before Spring's post-construct phase 【发布时间】:2019-03-24 10:58:23 【问题描述】:

基本上,问题就在标题中。 我遇到了一个问题,在构建后阶段,我的 bean(即在现在正在经历构建后阶段的 bean 中自动装配)已经被模拟,但是 Mockito.when() 描述的所有行为都不起作用,所有调用返回null

在搜索时我找到了this 解决方案。

但是是否有可能在不使用任何 3rd 方库的情况下使其工作?

测试类:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@ContextConfiguration(classes = TestApplicationConfiguration.class)
public class ServiceTest 

@Autowired
@Qualifier("test")
private PCLPortType pclPortType;

@MockBean
private ClearingHelper сlearingHelper;

@MockBean
private OrganizationCacheRepository organizationCacheRepository;

@Before
public void setup() throws Exception

     OperationResultWithOrganizationSystemIdMappingList res = new OperationResultWithOrganizationSystemIdMappingList();

    when(clearingHelper.getOrgIdSystemIdMapping(any(Keycloak.class))).thenReturn(res);



@Test
public void test() throws Exception
    pclPortType.call("123");


测试配置:

@TestConfiguration
public class TestApplicationConfiguration 

@Bean(name = "test")
public PCLPortType pclPortTypeForTest() throws JAXBException 
   ...


@Bean
public Keycloak keycloak() 
    return Mockito.mock(Keycloak.class);


我想获取模拟 bean 的组件:

@Component
public class OrganizationCacheJob 
private static final Logger logger = 
LogManager.getLogger(OrganizationCacheJob.class);

private final ObjectFactory<Keycloak> factory;
private final ClearingHelper clearingHelper;
private final OrganizationCacheRepository organizationCacheRepository;

@Autowired
public OrganizationCacheJob(ObjectFactory<Keycloak> factory,
                            ClearingHelper clearingHelper,
                            OrganizationCacheRepository organizationCacheRepository) 
    this.factory = factory;
    this.clearingHelper = ClearingHelper;
    this.organizationCacheRepository = organizationCacheRepository;



@PostConstruct
public void updateCacheRepository() 
    doUpdateCacheRepository();


@Scheduled(cron = "$organization.cache.schedule")
public void start() 
    logger.info("Starting update organization cache.");
    doUpdateCacheRepository();
    logger.info("Job finished.");


private void doUpdateCacheRepository() 
    try 
        Keycloak keycloak = factory.getObject();
        OperationResultWithOrganizationSystemIdMappingList orgIdSystemIdMapping = clearingHelper.getOrgIdSystemIdMapping(keycloak);
        if (orgIdSystemIdMapping != null) 
            orgIdSystemIdMapping.getContent().forEach(o -> organizationCacheRepository.saveOrgIdsSystemsIdsMappings(o.getOrgId(), o.getId()));
            logger.debug("Was saved  orgIds", orgIdSystemIdMapping.getContent().size());
        
     catch (Exception e) 
        logger.error("Error fetching whole mapping for org and systems ids. Exception: ", e);
    


所以,在OrganizationCacheJob 的构建后阶段,我想在调用clearingHelper 时得到res,但我得到的是null

ClearingHelper 是一个常规 Spring bean,使用公共方法标记为 @Component

【问题讨论】:

@Antoniossss 完成,请看一下。我认为 bean 本身的代码没什么大不了的,但如果你认为我也应该添加它 - 让我知道 :) doUpdateCacheRepository()移动到构造函数并删除updateCacheRepository()方法会发生什么?顺便说一句,你 don't need @Autowired 在构造函数上。 我在测试类代码中没有看到vdvClearingHelper。也许这就是罪魁祸首。 你的意思是 clearingHelper 在 doUpdateCacheRepository() 方法中为空吗? 【参考方案1】:

啊,好吧,我刚刚意识到 - 当您开始测试用例时,整个环境首先启动并运行,然后您进入测试阶段。因此,转换为您的案例 - 首先您调用了注入和后构造,然后完成了 @Before 方法,从而得到了结果。

如您所见,代码比您在原始帖子中可以输入的所有单词都多。

如果可能,请使用间谍而不是模拟。如果无法构建它,您将不得不重新设计测试以不依赖 post 构建。

在这种情况下,由于您希望每个测试用例都具有相同的构造后行为,因此请为给定的模拟提供自己的工厂方法(就像您对 keycloak 所做的那样)并将 when-doReturn 移到那里。保证在后期构建之前发生。

【讨论】:

我最终为你所说的模拟提供了工厂方法,效果很好。谢谢!

以上是关于在 Spring 的构建后阶段之前注入 mock的主要内容,如果未能解决你的问题,请参考以下文章

C#单元测试--如何使用moq.mock进行依赖注入

spring源码之BeanPostProcessor

将环境变量注入图像的构建阶段

Autofac 集成测试 在 ConfigureContainer 之后进行 Mock 注入

阶段3 2.Spring_04.Spring的常用注解_5 自动按照类型注入

SpringBoot 单元测试利器——Mockito