本文介绍了如何使用 Testcontainers GCloud 模块为使用 Spring Boot 的 Spanner 数据库编写集成测试。
Testcontainers是一个库,有助于在特定模块(数据库、Kafka、Redis)Docker 容器中编写可靠的集成测试。一旦测试的执行结束,容器就会被完全销毁。
如果您的应用程序使用 Google Cloud 组件(如Spanner、Firestore等)来编写高效的集成测试,Testcontainers为 Google Cloud Platform 的 Cloud SDK提供了GCloud 模块。
本文介绍了如何使用 Testcontainers GCloud 模块为使用Spring Boot的 Spanner 数据库编写集成测试。
什么是模拟器?
模拟器旨在在本地/离线环境中运行,它模拟像 Spanner 这样的云服务的行为。用于本地测试,无需连接云环境。
什么是 Testcontainers GCloud 模块?
GCloud 模块有助于轻松为Pub/Sub和 Spanner 等服务启动 Google Cloud 模拟器。根据 Testcontainers 文档,GCloud 模块(截至目前)支持 Bigtable、Datastore、Firestore、Spanner 和 Pub/Sub 模拟器。我们可以假设 GCloud API 是Cloud SDK模拟器和Docker的包装器。
集成测试
考虑一个用 Spring Boot 编写的演示订单服务,用于下订单。订单存储在名为“订单”的 Spanner 数据库表中。请参阅扳手测试容器演示 示例服务代码。
为了使用 Testcontainers、GCloud 和 Junit5 编写集成测试,首先,我们需要在“build.gradle”中导入以下库。
//TestContainers
testImplementation 'org.testcontainers:testcontainers:1.17.3'
testImplementation group: 'org.testcontainers', name: 'junit-jupiter', version: '1.17.3'
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.9.0'
implementation 'org.testcontainers:gcloud:1.17.3'
下一步是使用该SpannerEmulatorContainer实例启动 Spanner Emulator。
@Container
private static final SpannerEmulatorContainer spannerEmulatorContainer =
new SpannerEmulatorContainer(
DockerImageName.parse("gcr.io/cloud-spanner-emulator/emulator:1.1.1"));
SpannerEmulatorContainer被声明为静态的,因为随后启动了一个容器,并且它在所有测试方法中共享。
@DynamicPropertySource用于覆盖属性。就像我们对 MySQLContainer 和 PostgresContainer 所做的那样,我们让 Testcontainers 创建 URL、用户名和密码以避免错误。同样,在 Spanner 的情况下,我们让 Testcontainers 为我们创建模拟器主机。
@DynamicPropertySource
static void emulatorProperties(DynamicPropertyRegistry registry) {
registry.add(
"spring.cloud.gcp.spanner.emulator-host", spannerEmulatorContainer::getEmulatorGrpcEndpoint);
}
在本地,如果我们运行 Spring Boot 应用程序并且错过了配置云“服务帐户”密钥或凭据,我们最终会得到以下信息:
The Application Default Credentials are not available.
They are available if running in Google Compute Engine.
Otherwise, the environment variable GOOGLE_APPLICATION_CREDENTIALS must be defined pointing to a file defining the credentials
为了在我们的测试中避免这个错误,我们需要定义NoCredentialsProvider. 这是因为,为了测试,不需要使用凭证:让Bootstrap使用NoCredentialsProvider实例。
@TestConfiguration
static class EmulatorConfiguration {
@Bean
NoCredentialsProvider googleCredentials() {
return NoCredentialsProvider.create();
}
}
这是设置SpannerEmulatorContainer. 下一步是创建 Spanner 实例、数据库和表。
private InstanceId createInstance(Spanner spanner) throws InterruptedException, ExecutionException {
InstanceConfigId instanceConfig = InstanceConfigId.of(PROJECT_ID, "emulator-config");
InstanceId instanceId = InstanceId.of(PROJECT_ID, INSTANCE_ID);
InstanceAdminClient insAdminClient = spanner.getInstanceAdminClient();
Instance instance = insAdminClient
.createInstance(
InstanceInfo
.newBuilder(instanceId)
.setNodeCount(1)
.setDisplayName("Test instance")
.setInstanceConfigId(instanceConfig)
.build()
)
.get();
return instanceId;
}
private void createDatabase(Spanner spanner) throws InterruptedException, ExecutionException {
DatabaseAdminClient dbAdminClient = spanner.getDatabaseAdminClient();
Database database = dbAdminClient
.createDatabase(
INSTANCE_ID,
"order_schema",
Arrays.asList("CREATE TABLE Orders (orderId INT64 NOT NULL, name STRING(255), order_status STRING(255)) PRIMARY KEY (orderId)")
)
.get();
}
private Spanner spanner(){
SpannerOptions options = SpannerOptions
.newBuilder()
.setEmulatorHost(spannerEmulatorContainer.getEmulatorGrpcEndpoint())
.setCredentials(NoCredentials.getInstance())
.setProjectId(PROJECT_ID)
.build();
Spanner spanner = options.getService();
return spanner;
}
现在我们准备好运行我们的集成测试了。
@Test
public void testOrders() throws ExecutionException, InterruptedException {
//Create Spanner Instance, DB, Table
Spanner spanner = spanner();
InstanceId instanceId = createInstance(spanner);
createDatabase(spanner);
//Create Order and Save
Order order = new Order();
order.setOrder_status("COMPLETED");
order.setName("Order1");
String message = this.orderService.save(order);
assertEquals("Order Saved Successfully", message);
//Validate
List<Order> orders = this.orderService.findOrdersByName("Order1");
assertTrue(orders.size() == 1);
assertTrue(orders.get(0).getOrder_status().equals("COMPLETED"));
}
包起来
Testcontainers 有助于编写可靠的集成测试。易于实施;另外,文档清晰明了。Testcontainers GCloud 使用 Google Cloud 组件简化了服务的开发,因为我们可以避免连接到云并在本地执行集成测试。
本文暂时没有评论,来添加一个吧(●'◡'●)