spring-boot-@mockbean在测试用例中创建副本

sirbozc5  于 2021-07-15  发布在  Java
关注(0)|答案(1)|浏览(342)

我试图“嘲笑”一个豆子 @SpringBootTest .

@SpringBootTest
@ActiveProfiles("test")
@RunWith(SpringRunner.class)
public class SalesSummaryServiceTest {

    @MockBean
    private SalesSummaryMapper summmaryMapper;

    @Autowired
    private SalesSummaryService salesSummmaryService;

    @Test
    public void testGetMonthlySummary() {

        // Removed for brevity

        given(this.summmaryMapper.selectByPropDate(testSchema, testProp, testDate, testEnd)).willReturn(List.of(testSum, testSum));     
        var res = this.salesSummmaryService.getMonthlySummary(test, testProp, testDate.getMonthValue(), testDate.getYear());

        // Removed for brevity

    }

}

但是,测试失败(无法加载应用程序上下文),因为找不到唯一bean:

Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.midamcorp.data.dal.SalesSummaryMapper' available: expected single matching bean but found 2: salesSummaryMapper,com.midamcorp.data.dal.SalesSummaryMapper#0

被测班级是一个基础班 @Service 实施:

@Service
public class SalesSummaryServiceImpl implements SalesSummaryService {

    private SalesSummaryMapper salesSumMapper;

    @Autowired
    public SalesSummaryServiceImpl(SalesSummaryMapper salesSumMapper) {
        this.salesSumMapper = salesSumMapper;
    }

    @Override
    public List<SaleSummary> getMonthlySummary(PropType propType, String propId, int monthVal, int yearVal) {
        var startDate = LocalDate.of(yearVal, monthVal, 1);
        var endDate = startDate.with(TemporalAdjusters.lastDayOfMonth());
        return this.salesSumMapper.selectByPropDate(propType.getSchema(), propId, startDate, endDate);
    }
}

这个 SaleSummaryMapper dependency是一个简单的mybatisMap器:

import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface SalesSummaryMapper {
}

我理解基本错误(例如,spring无法确定使用哪个bean,因为两个bean可以满足依赖性)。我不明白的是为什么“真正的”mapperbean没有被 @MockBean 当,根据文档:“上下文中定义的相同类型的任何现有单个bean将被mock替换”。我错过了什么?谢谢!

sqyvllje

sqyvllje1#

我认为问题可能与mybatis如何在spring上下文中注册bean有关。接口 SalesSummaryMapper 不是在spring上下文中创建的bean的类型。从mybatis文档示例中,我看到mapper实际上是 MapperFactoryBean :

<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
  <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

我假设当您使用任何类型的扫描器来注册带有 @Mapper 注解。
所以 @MockBean 不替换Map器是因为它们不属于同一类型。
编辑:我收回上面的内容。豆子将是相同的类型,因为 MapperFactoryBean 执行Spring的 FactoryBean . 可能在创建模拟bean的那一刻,工厂还没有创建bean?
您可以首先用实际的 @Component 豆子。如果可以的话,你应该试着用名字而不是类型来模拟。
在mybatis文档中,您可以阅读:
发现的Map器将使用spring自动检测组件的默认命名策略进行命名(请参阅spring参考文档(核心技术-命名自动检测组件-)。也就是说,如果找不到注解,它将使用Map器的未大写的非限定类名。但如果找到@component或jsr-330@命名注解,它将从注解中获取名称。注意,您可以将annotation属性设置为org.springframework.stereotype.component、javax.inject.named(如果您有jse6)或您自己的annotation(必须对其本身进行注解),这样annotation既可以作为标记,也可以作为名称提供者。
基于以上的潜在解决方案可以是将Map器另外标记为named @Component :

@Mapper
@Component("salesSummaryMapper")
public interface SalesSummaryMapper {
}

在测试中使用 @Qualifier ```
@MockBean
@Qualifier("salesSummaryMapper")
private SalesSummaryMapper summmaryMapper;

相关问题