1. 引言
在这篇文章中,我们将学习如何在Java应用程序中模拟亚马逊S3(简单存储服务)来编写集成测试。
为了演示其工作原理,我们将创建一个使用AWS SDK与S3交互的CRUD(创建、读取、更新、删除)服务,然后为每个操作编写集成测试,使用模拟的S3服务。
2. S3概述
亚马逊简单存储服务(S3)是由亚马逊网络服务(AWS)提供的高度可扩展和安全的云存储服务。它采用对象存储模型,允许用户从网络上的任何地方存储和检索数据。
通过REST风格的API访问此服务,AWS提供了Java应用程序SDK,用于执行如创建、列出和删除S3桶和对象等操作。
接下来,让我们开始使用AWS SDK创建Java的S3 CRUD服务,并实现创建、读取、更新和删除操作。
3. 示例S3 CRUD Java服务
在开始使用S3之前,我们需要在项目中添加AWS SDK的依赖:
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
<version>2.20.52</version>
</dependency>
要查看最新版本,请参阅Maven中央仓库。
接下来,我们创建带有software.amazon.awssdk.services.s3.S3Client
依赖的S3CrudService
类:
class S3CrudService {
private final S3Client s3Client;
public S3CrudService(S3Client s3Client) {
this.s3Client = s3Client;
}
// ...
}
现在我们有了服务,让我们使用AWS SDK提供的S3Client
API来实现createBucket()
、createObject()
、getObject()
和deleteObject()
操作:
void createBucket(String bucketName) {
// build bucketRequest
s3Client.createBucket(bucketRequest);
}
void createObject(String bucketName, File inMemoryObject) {
// build putObjectRequest
s3Client.putObject(request, RequestBody.fromByteBuffer(inMemoryObject.getContent()));
}
Optional<byte[]> getObject(String bucketName, String objectKey) {
try {
// build getObjectRequest
ResponseBytes<GetObjectResponse> responseResponseBytes = s3Client.getObjectAsBytes(getObjectRequest);
return Optional.of(responseResponseBytes.asByteArray());
} catch (S3Exception e) {
return Optional.empty();
}
}
boolean deleteObject(String bucketName, String objectKey) {
try {
// build deleteObjectRequest
s3Client.deleteObject(deleteObjectRequest);
return true;
} catch (S3Exception e) {
return false;
}
}
现在我们已经创建了S3操作,接下来将学习如何使用模拟的S3服务来实现集成测试。
4. 使用S3Mock进行集成测试
对于本教程,我们选择了由Adobe提供的、基于Apache V2许可证的S3Mock库。S3Mock是一个轻量级服务器,实现了Amazon S3 API中最常用的操作。有关支持的S3操作,可以查看S3Mock仓库readme文件中的相关部分。
库开发者建议单独运行S3Mock服务,最好使用提供的Docker容器。
遵循这一建议,我们将使用Docker和Testcontainers来为集成测试运行S3Mock服务。
4.1. 依赖项
接下来,让我们添加运行S3Mock所需的必要依赖,以及Testcontainers:
<dependency>
<groupId>com.adobe.testing</groupId>
<artifactId>s3mock</artifactId>
<version>3.3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.adobe.testing</groupId>
<artifactId>s3mock-testcontainers</artifactId>
<version>3.3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.19.4</version>
<scope>test</scope>
</dependency>
可以在Maven Central、s3mock-testcontainers、junit-jupiter上查看最新的版本链接。
4.2. 配置
作为前提条件,我们必须有一个运行的Docker环境,以确保Test Containers能够启动。
当我们在集成测试类上使用@TestContainers
和@Container
注解时,会从注册表拉取并启动最新的S3MockContainer
Docker镜像:
@Testcontainers
class S3CrudServiceIntegrationTest {
@Container
private S3MockContainer s3Mock = new S3MockContainer("latest");
}
在运行集成测试之前,让我们在@BeforeEach
生命周期方法中创建一个S3Client
实例:
@BeforeEach
void setUp() {
var endpoint = s3Mock.getHttpsEndpoint();
var serviceConfig = S3Configuration.builder()
.pathStyleAccessEnabled(true)
.build();
var httpClient = UrlConnectionHttpClient.builder()
.buildWithDefaults(AttributeMap.builder()
.put(TRUST_ALL_CERTIFICATES, Boolean.TRUE)
.build());
s3Client = S3Client.builder()
.endpointOverride(URI.create(endpoint))
.serviceConfiguration(serviceConfig)
.httpClient(httpClient)
.build();
}
在setup()
方法中,我们使用S3Client
接口提供的构建器初始化了一个实例。在这个初始化过程中,我们为以下参数配置了设置:
- endpointOverwrite: 这个参数用于定义S3模拟服务的地址。
- pathStyleAccessEnabled: 我们在服务配置中将这个参数设置为
true
。 - TRUST_ALL_CERTIFICATES: 另外,我们为
httpClient
实例配置了所有证书信任,通过将TRUST_ALL_CERTIFICATES
设置为true
。
4.3. 编写集成测试S3CrudService
在基础设施设置完成后,让我们为S3CrudService
操作编写一些集成测试。
首先,我们创建一个桶并验证其成功创建:
var s3CrudService = new S3CrudService(s3Client);
s3CrudService.createBucket(TEST_BUCKET_NAME);
var createdBucketName = s3Client.listBuckets().buckets().get(0).name();
assertThat(TEST_BUCKET_NAME).isEqualTo(createdBucketName);
创建成功后,我们将上传一个新的S3对象。
为此,我们首先使用FileGenerator
生成一个字节数组,然后createObject()
方法将其保存为已创建的桶中的对象:
var fileToSave = FileGenerator.generateFiles(1, 100).get(0);
s3CrudService.createObject(TEST_BUCKET_NAME, fileToSave);
接下来,让我们调用getObject()
方法,传入已保存文件的文件名,确认对象是否确实保存在S3中:
var savedFileContent = s3CrudService.getObject(TEST_BUCKET_NAME, fileToSave.getName());
assertThat(Arrays.equals(fileToSave.getContent().array(), savedFileContent)).isTrue();
最后,测试deleteObject()
是否按预期工作。首先,我们调用deleteObject()
方法,传入桶名和目标文件名。然后再次调用getObject()
并检查结果是否为空:
s3CrudService.deleteObject(TEST_BUCKET_NAME,fileToSave.getName());
var deletedFileContent = s3CrudService.getObject(TEST_BUCKET_NAME, fileToSave.getName());
assertThat(deletedFileContent).isEmpty();
5. 结论
在这篇教程中,我们学习了如何使用S3Mock库编写依赖于AWS S3服务的集成测试,通过模拟实际的S3服务。
我们首先实现了一个基本的CRUD服务,它在S3上创建、读取和删除对象,然后使用S3Mock库编写了集成测试。
如往常一样,本文的完整实现可在GitHub上找到。