如何在Spring Boot中实现集成测试?

栏目: Java · 发布时间: 6年前

内容简介:集成测试可以验证代码库中的整个调用路径,不幸的是,这种测试方法很难在Spring Boot应用程序中使用。本案例探索如何克服这种困难:我们将使用一个简单的REST服务示例,它具有我们连接使用的单个SQL数据库依赖项spring-boot-starter-data-jpa,一个预定义的Spring组件包,可以通过JPA轻松访问SQL数据,以及h2一个用Java编写的免费SQL数据库。案例源码

集成测试可以验证代码库中的整个调用路径,不幸的是,这种测试方法很难在Spring Boot应用程序中使用。本案例探索如何克服这种困难:

我们将使用一个简单的REST服务示例,它具有我们连接使用的单个 SQL 数据库依赖项spring-boot-starter-data-jpa,一个预定义的Spring组件包,可以通过JPA轻松访问SQL数据,以及h2一个用 Java 编写的免费SQL数据库。

案例源码 这里

下面是它包含的组件,所有示例代码都是使用Java 10编写的:

SpringBootApplication
<b>class</b> SlalomiteApplication

@RestController
<b>class</b> SlalomiteController <font><i>// depends on SlalomiteService</i></font><font>

@Service
<b>class</b> SlalomiteService </font><font><i>// depends on SlalomiteRepository</i></font><font>

<b>interface</b> SlalomiteRepository <b>extends</b> CrudRepository<Slalomite, Long>
</font>

问题

看看CrudRepository存储库,虽然代码非常简单,但是如何模拟Spring提供的组件并不明显。这使得很难验证依赖于此存储库类的应用程序逻辑是否正确。

<b>import</b> org.springframework.data.repository.CrudRepository;

<b>public</b> <b>interface</b> SlalomiteRepository <b>extends</b> CrudRepository<Slalomite, Long> {
}

使用Spring Boot @DataJpaTest和@SpringBootTest(webEnvironment = ...)注释,可以实现模拟数据库并为此应用程序编写集成测试。该集成测试看起来像这样:

@RunWith(SpringRunner.<b>class</b>)
@SpringBootTest(<b>class</b>es = SlalomiteApplication.<b>class</b>, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@DataJpaTest
<b>public</b> <b>class</b> SlalomiteIntegrationTestBroken {
    @Autowired
    <b>private</b> TestRestTemplate restTemplate;

    @Autowired
    <b>private</b> TestEntityManager entityManager;

    @Test
    <b>public</b> <b>void</b> getSlalomites_ShouldReturnAdam() {
        <b>var</b> slalomite = <b>new</b> Slalomite(<font>"Adam"</font><font>, Date.from(Instant.now()));
        <b>this</b>.entityManager.persist(slalomite);

        <b>var</b> response = restTemplate.getForEntity(</font><font>"/api/v1/slalomites"</font><font>, String.<b>class</b>);

        assertTrue(response.getBody().contains(</font><font>"Adam"</font><font>));
    }
</font>

}

通过结合Spring教程,Spring指南和许多博客中的想法,这看起来可能是正确的,但不幸的是,在运行它时会遇到以下异常:

java.lang.IllegalStateException: Failed to load ApplicationContext... Caused by: org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.

这里的问题是@DataJpaTest的。来自 javadoc 解释:

使用此批注将禁用完全自动配置,而只应用与JPA测试相关的配置。

问题发生原因:@DataJpaTest只启用运行持久层所需的组件并禁用所有其他组件。这包括Spring用于启动servlet容器的组件。由于@SpringBootTest尝试启动servlet容器,因此在无法找到启动servlet所需的bean时会产生上述异常。

解决方案

解决方案非常简单,实际上在同一个@DataJpaTestjavadoc中暗示过,即使它没有在Spring文档或在线的许多地方提到过:

如果您要加载完整的应用程序配置,且使用了嵌入式数据库,则应考虑将@SpringBootTest与@AutoConfigureTestDatabase结合使用而不是使用此注释。

使用@AutoConfigureTestDatabase 时,我们没有提供@DataJpaTest提供的许多便利,例如,前面的例子中使用的TestEntityManager是不可用的;我们也没有得到事务测试的好处,这意味着我们必须更加谨慎地清理自己。

但是,还是保留了关键优势 - 在运行集成测试时,应用程序将连接到Spring创建的内存数据库,而不是我们的实际数据库实例。这使我们能够从数据库中读取和写入,而无需担心预先存在的数据或担心创建会影响其他应用程序或用户的数据,从而使测试更安全地作为CI / CD管道的一部分运行并且更具可重复性。

由于我们无法访问TestEntityManager此处,因此我们需要直接编写SQL来设置和清理数据库,或者我们需要在实际调用控制器方法之前利用我们的SlalomiteRepository bean来执行写操作。

这里演示我们将使用第二个选项。虽然它不像直接编写SQL那样具有“黑盒子”的方法,但它更简单,更不易碎。新的、通过测试的代码如下:

@RunWith(SpringRunner.<b>class</b>)
@SpringBootTest(<b>class</b>es = SlalomiteApplication.<b>class</b>, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureTestDatabase
<b>public</b> <b>class</b> SlalomiteIntegrationTest {

    @Autowired
    <b>private</b> TestRestTemplate restTemplate;

    @Autowired
    <b>private</b> SlalomiteRepository repo;

    @After
    <b>public</b> <b>void</b> cleanup() {
        repo.deleteAll();
    }

    @Test
    <b>public</b> <b>void</b> getSlalomites_ShouldReturnAdam() {
        <b>var</b> slalomite = <b>new</b> Slalomite(<font>"Adam"</font><font>, Date.from(Instant.now()));
        repo.save(slalomite);

        <b>var</b> response = restTemplate.getForEntity(</font><font>"/api/v1/slalomites"</font><font>, String.<b>class</b>);

        assertTrue(response.getBody().contains(</font><font>"Adam"</font><font>));
    }
}
</font>

请注意添加cleanup方法。如果在每次测试后未清除对数据的更改,则最终会出现意外故障,因为不满足给定测试的前提条件。在实践中,由于您的测试运行员选择您的单元测试,这些失败可能会随机结束。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

微信营销与运营

微信营销与运营

秦阳、秋叶 / 人民邮电出版社 / 2016-12-1 / 39.80

《微信营销与运营》共分七章。第1章重点介绍了微信营销的概念、价值和特征,引导读者全面认识微信营销;第2章介绍了个人微信号的运营技巧和手法;第3章重点介绍了微信公众平台的基础操作入门,申请适合自己的公众平台类型并进行基本设置;第4章介绍了微信运营的规划策略,落实公众号的定位、内容问题;第5章介绍微信运营中包括排版、增加粉丝、提升阅读量等运营实战中的经验和手法,并了解微信运营的整个运营框架体系;第6章......一起来看看 《微信营销与运营》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

SHA 加密
SHA 加密

SHA 加密工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具