单元测试中的 Spring Boot 数据源

Posted

技术标签:

【中文标题】单元测试中的 Spring Boot 数据源【英文标题】:Spring Boot Datasource in unit tests 【发布时间】:2018-09-08 14:17:31 【问题描述】:

我有一个简单的 Spring Boot Web 应用程序,它从数据库中读取数据并返回 JSON 响应。我有以下测试配置:

@RunWith(SpringRunner.class)
@SpringBootTest(classes=MyApplication.class, properties="spring.config.name=myapp")
@AutoConfigureMockMvc
public class ControllerTests 
    @Autowired
    private MockMvc mvc;
    @MockBean
    private ProductRepository productRepo;
    @MockBean
    private MonitorRepository monitorRepo;

    @Before
    public void setupMock() 
        Mockito.when(productRepo.findProducts(anyString(), anyString()))
        .thenReturn(Arrays.asList(dummyProduct()));     
    

    @Test
    public void expectBadRequestWhenNoParamters() throws Exception     
        mvc.perform(get("/products"))
                .andExpect(status().is(400))
                .andExpect(jsonPath("$.advice.status", is("ERROR")));
    

    //other tests

我有一个在应用程序的主配置中配置的 DataSource bean。当我运行测试时,Spring 尝试加载上下文并失败,因为数据源来自 JNDI。一般来说,我想避免为此测试创建数据源,因为我已经模拟了存储库。

运行单元测试时是否可以跳过数据源的创建?

在内存数据库中进行测试不是一个选项,因为我的数据库创建脚本具有特定的结构,无法从 classpath:schema.sql 轻松执行

编辑 数据源定义在MyApplication.class

    @Bean
    DataSource dataSource(DatabaseProeprties databaseProps) throws NamingException 
       DataSource dataSource = null;
       JndiTemplate jndi = new JndiTemplate();
       setJndiEnvironment(databaseProps, jndi);
       try 
           dataSource = jndi.lookup(databaseProps.getName(), DataSource.class);
        catch (NamingException e) 
           logger.error("Exception loading JNDI datasource", e);
           throw e;
       
       return dataSource;
   

【问题讨论】:

您的数据源是否通过自动配置进行配置? @wjans 不,它是主配置中的一个 bean。查看我的编辑。 你不能简单地将数据源添加为@MockBean DataSource dataSource 吗?认为它的优点是您的生产代码执行 JNDI 查找甚至不会被执行。 【参考方案1】:

尝试将您的数据源也添加为@MockBean

@MockBean
private DataSource dataSource

这样,Spring 将为您执行替换逻辑,其优势是您的生产代码 bean 创建甚至不会被执行(没有 JNDI 查找)。

【讨论】:

这也是我的做法。您可以将 MockBean 添加到 TestConfig.class 以最大程度地减少代码重复。 很好的建议,在测试类中添加 @MockBean(name = "your bean name")【参考方案2】:

由于您正在加载配置类 MyApplication.class 数据源 bean 将被创建,请尝试将数据源移动到另一个未用于测试的 bean 中,确保为测试加载的所有类不依赖于数据源。 或 在您的测试中创建一个标有@TestConfiguration 的配置类,并将其包含在SpringBootTest(classes=TestConfig.class) 模拟数据源中,例如

@Bean
public DataSource dataSource() 
    return Mockito.mock(DataSource.class);

但这可能会失败,因为对该模拟数据源的连接方法调用将返回 null,在这种情况下,您必须创建一个内存数据源,然后模拟 jdbcTemplate 和其余依赖项。

【讨论】:

很方便,谢谢。在日志中添加 TestConfiguration 后,我得到:Overriding bean definition for bean 'dataSource' with a different definition。我还为集成测试创建了另一个定义真实数据源的 TestConfiguration。 这不会有潜在的风险,即覆盖将以不同的顺序发生吗?认为依赖这个不是一个好主意,认为你应该给模拟的 bean 一个不同的名字,并用 @Primary 注释它以确保。仍然有两个bean都会被创建的缺点。 我同意@wjans

以上是关于单元测试中的 Spring Boot 数据源的主要内容,如果未能解决你的问题,请参考以下文章

spring boot单元测试spring context重复加载问题

单元测试中的 Spring Boot 数据源

Spring boot 1.4.2 单元测试配置

Spring Boot 中的单元测试

Spring Boot - DI - 单元测试

单元测试中的 Spring Boot @Autowired 返回 NullPointerException