Testcontainers是一个Java库,它支持JUnit测试,提供公共数据库、SeleniumWeb浏览器或任何能够在Docker容器中运行的轻量级、一次性实例。java
测试容器使如下类型的测试更加容易:mysql
数据访问层集成测试:web
使用MySQL,PostgreSQL或Oracle数据库的容器化实例测试您的数据访问层代码,但无需在开发人员的计算机上进行复杂的设置,而且测试将始终从已知的数据库状态开始,避免“垃圾”数据的干扰。也可使用任何其余能够容器化的数据库类型。spring
应用程序集成测试:sql
用于在具备相关性(例如数据库,消息队列或Web服务器)的短时间测试模式下运行应用程序。数据库
UI /验收测试:浏览器
使用与Selenium兼容的容器化Web浏览器进行自动化UI测试。每一个测试均可以获取浏览器的新实例,而无需担忧浏览器状态,插件版本或浏览器自动升级。您将得到每一个测试会话或测试失败的视频记录。服务器
更多:app
能够签出各类贡献的模块,或使用 GenericContainer做为基础建立本身的自定义容器类。框架
Testcontainers提供了多种现成的与测试关联的应用程序容器,以下图:
在本文中,将演示集成postgresql容器和mockserver容器的测试。
Testcontainers必要条件:
1.Docker
2.支持的JVM测试框架:JUnit4,JUnit5,spock...
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.12.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<!--指定数据库名称,mysql,mariadb等等-->
<artifactId>postgresql</artifactId>
<version>1.12.5</version>
<scope>test</scope>
</dependency>
复制代码
在项目的src/test/resources/application.yml文件中配置postgresql相关信息
#将驱动程序设置为org.testcontainers.jdbc.ContainerDatabaseDriver,它是一个Testcontainers JDBC代理驱动程序。初始化数据源时,此驱动程序将负责启动所需的Docker容器。
spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver
#将JDBC URL设置为JDBC:tc:<database image>:<version>:///以便Testcontainers知道要使用哪一个数据库。
#TC_INITSCRIPT=指定的数据库初始化的脚本文件位置
spring.datasource.url=jdbc:tc:postgresql:9.6:///?TC_INITSCRIPT=file:src/main/resources/init_db.sql
#将方言明确设置为数据库的方言实现,不然在启动应用程序时会收到异常。当您在应用程序中使用JPA时(经过Spring Data JPA),此步骤是必需的
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL9Dialect
复制代码
为了在@DataJpaTest中使用TC,您须要确保使用了应用程序定义的(自动配置的)数据源。您能够经过使用@AutoConfigureTestDatabase注释测试来轻松完成此操做,以下所示:
@RunWith(SpringJUnit4ClassRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class OwnerRepositoryTests {
@Autowired
private OwnerRepository ownerRepository;
@Test
void findAllReturnsJohnDoe() { // as defined in tc-initscript.sql
var owners = ownerRepository.findAll();
assertThat(owners.size()).isOne();
assertThat(owners.get(0).getFirstName()).isEqualTo("John");
assertThat(owners.get(0).getLastName()).isEqualTo("Doe");
}
}
复制代码
以上测试将使用Testcontainers提供的postgresql容器进行测试,从而排除了外部环境对测试的干扰。
当须要用本地数据库进行集成测试时,咱们只要使用@SpringBootTest替换如上两个注解便可:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class OwnerResourceTests {
@Autowired
WebApplicationContext wac;
@Test
void findAllReturnsJohnDoe() throws Exception {
given()
.webAppContextSetup(wac)
.when()
.get("/owners")
.then()
.status(HttpStatus.OK)
.body(
"_embedded.owners.firstName", containsInAnyOrder("John"),
"_embedded.owners.lastName", containsInAnyOrder("Doe")
);
}
}
复制代码
以上测试将使用真实运行环境的数据库进行测试。
Mock Server可用于经过将请求与用户定义的指望进行匹配来模拟HTTP服务。
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mockserver</artifactId>
<version>1.12.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mock-server</groupId>
<artifactId>mockserver-netty</artifactId>
<version>5.5.4</version>
</dependency>
<dependency>
<groupId>org.mock-server</groupId>
<artifactId>mockserver-client-java</artifactId>
<version>5.5.4</version>
</dependency>
复制代码
//建立一个MockServer容器
@Rule
public MockServerContainer mockServer = new MockServerContainer();
复制代码
以及使用Java MockServerClient设置简单的指望。
new MockServerClient(mockServer.getContainerIpAddress(), mockServer.getServerPort())
.when(request()
.withPath("/person")
.withQueryStringParameter("name", "peter"))
.respond(response()
.withBody("Peter the person!"));
//...当一个get请求至'/person?name=peter' 时会返回 "Peter the person!"
复制代码
测试(使用restassured进行测试):
RestAssured.baseURI = "http://" + mockServer.getContainerIpAddress();
RestAssured.port = mockServer.getServerPort();
given().queryParam("name", "peter")
.get("/person")
.then()
.statusCode(HttpStatus.OK.value())
.body(is("Peter the person!"));
复制代码
完整代码以下:
@RunWith(SpringJUnit4ClassRunner.class)
public class OneTests {
@Rule
public MockServerContainer mockServer = new MockServerContainer();
@Test
public void v() {
RestAssured.baseURI = "http://" + mockServer.getContainerIpAddress();
RestAssured.port = mockServer.getServerPort();
new MockServerClient(mockServer.getContainerIpAddress(), mockServer.getServerPort())
.when(request()
.withPath("/person")
.withQueryStringParameter("name", "peter"))
.respond(response()
.withBody("Peter the person!"));
given().queryParam("name", "peter")
.get("/person")
.then()
.statusCode(HttpStatus.OK.value())
.body(is("Peter the person!"));
}
}
复制代码
Testcontainers轻松的解决了集成测试时测试代码与本地组件耦合,从而出现各类意外失败的问题(好比本地数据库中存在脏数据影响到了集成测试,多个集成测试同时运行时相互干扰致使测试结果意外失败)。笔者以前专门为集成测试准备了一套数据库,使数据和其余环境隔离掉,但仍是会遇到多个集成测试一块儿跑相互干扰的问题,Testcontainers轻松的解决了笔者的问题。
🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟🌟
欢迎访问笔者博客:blog.dongxishaonian.tech
关注笔者公众号,推送各种原创/优质技术文章 ⬇️