1. Introduction
1.导言
In this article, we’ll learn how to mock Amazon S3 (Simple Storage Service) to run integration tests for Java applications.
在本文中,我们将学习如何模拟 亚马逊 S3(简单存储服务)来运行 Java 应用程序的集成测试。
To demonstrate how it works, we’ll create a CRUD (create, read, update, delete) service that uses the AWS SDK to interact with the S3. Then, we’ll write integration tests for each operation using a mocked S3 service.
为了演示其工作原理,我们将创建一个使用 AWS SDK 与 S3 交互的 CRUD(创建、读取、更新、删除)服务。然后,我们将使用模拟的 S3 服务为每个操作编写集成测试。
2. S3 Overview
2.S3 概览
Amazon Simple Storage Service (S3) is a highly scalable and secure cloud storage service provided by Amazon Web Services (AWS). It uses an object storage model, allowing users to store and retrieve data from anywhere on the web.
亚马逊简单存储服务(S3)是亚马逊网络服务(AWS)提供的一种高度可扩展和安全的云存储服务。它使用 对象存储模型,允许用户从网络上的任何地方存储和检索数据。
The service is accessible by a REST-style API, and AWS provides an SDK for Java applications to perform actions like creating, listing, and deleting S3 buckets and objects.
该服务可通过 REST 风格的 API 访问,AWS 还为 Java 应用程序提供了 SDK,用于执行创建、列出和删除 S3 存储桶和对象等操作。
Next, let’s start creating the Java CRUD service for S3 using the AWS SDK and implement the create, read, update, and delete operations.
接下来,让我们开始使用 AWS SDK 为 S3 创建 Java CRUD 服务,并实现创建、读取、更新和删除操作。
3. Demo S3 CRUD Java Service
3.S3 CRUD Java 服务演示
Before we can start using S3, we need to add a dependency to AWS SDK into our project:
在开始使用 S3 之前,我们需要在项目中添加对 AWS SDK 的依赖:
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
<version>2.20.52</version>
</dependency>
To view the latest version, we can check Maven Central.
要查看最新版本,我们可以查看 Maven Central 。
Next, we create the S3CrudService class with software.amazon.awssdk.services.s3.S3Client as a dependency:
接下来,我们创建 S3CrudService 类,并将 software.amazon.awssdk.services.s3.S3Client 作为依赖关系:
class S3CrudService {
private final S3Client s3Client;
public S3CrudService(S3Client s3Client) {
this.s3Client = s3Client;
}
// ...
}
Now that we’ve created the service, let’s implement the createBucket(), createObject(), getObject(), and deleteObject() operations by using the S3Client API provided by AWS SDK:
现在我们已经创建了服务,让我们使用 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;
}
}
Now that we have the S3 operations created, let’s learn how to implement integration tests using a mocked S3 service.
现在我们已经创建了 S3 操作,让我们来学习如何使用模拟的 S3 服务实施集成测试。
4. Use S3Mock Library for Integration Testing
4.使用 S3Mock 库进行集成测试
For this tutorial, we have chosen to use the S3Mock library provided by Adobe under an open-source Apache V2 license. S3Mock is a lightweight server that implements the most commonly used operations of the Amazon S3 API. For the supported S3 operations, we can check the dedicated section in the S3Mock repository readme file.
在本教程中,我们选择使用 Adobe 根据开源 Apache V2 许可证提供的 S3Mock 库。S3Mock 是一个轻量级服务器,可实现 Amazon S3 API 中最常用的操作。关于支持的 S3 操作,我们可以查看 S3Mock 存储库自述文件中的专门部分。
The library developers recommend running the S3Mock service in isolation, preferably using the provided Docker container.
库开发人员建议隔离运行 S3Mock 服务,最好使用提供的 Docker 容器。
Following the recommendation, let’s use Docker and Testcontainers to run the S3Mock service for the integration tests.
根据建议,让我们使用 Docker 和 Testcontainers 为集成测试运行 S3Mock 服务。
4.1. Dependencies
4.1 依赖性
Next, let’s add the necessary dependencies to run S3Mock together with Testcontainers:
接下来,让我们添加必要的依赖项,以便与 Testcontainers 一起运行 S3Mock:
<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>
We can check the s3mock, s3mock-testcontainers, junit-jupiter links on Maven Central to view the latest version.
我们可以查看 Maven Central 上的 s3mock, s3mock-testcontainers, junit-jupiter 链接来查看最新版本。
4.2. Setup
4.2.设置
As a prerequisite, we must have a running Docker environment to ensure that Test Containers can be started.
作为前提条件,我们必须有一个正在运行的 Docker 环境,以确保可以启动测试容器。
When we use the @TestConainers and @Container annotations on the integration test class, the latest Docker image for S3MockContainer is pulled from the registry and started within the local Docker environment:
当我们在集成测试类上使用 @TestConainers 和 @Container 注解时,S3MockContainer 的最新 Docker 映像会从注册表中提取并在本地 Docker 环境中启动:
@Testcontainers
class S3CrudServiceIntegrationTest {
@Container
private S3MockContainer s3Mock = new S3MockContainer("latest");
}
Before running the integration test, let’s create an S3Client instance within the @BeforeEach lifecycle method:
在运行集成测试之前,让我们在 @BeforeEach lifecycle 方法中创建一个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();
}
In the setup() method, we initialized an instance of S3Client using the builder offered by the S3Client interface. Within this initialization, we specified configurations for the following parameters:
在 setup() 方法中,我们使用 S3Client 接口提供的构建器初始化了 S3Client 的实例。在初始化过程中,我们为以下参数指定了配置:
- endpointOverwrite: This parameter is configured to define the address of the S3 mocked service.
- pathStyleAccessEnabled: We set this parameter to true in the service configuration.
- TRUST_ALL_CERTIFICATES: Additionally, we configured an httpClient instance with all certificates trusted, indicated by setting TRUST_ALL_CERTIFICATES to true.
4.3. Writing Integration Test for the S3CrudService
4.3.编写 S3CrudService 的集成测试</em
As we finish with the infrastructure setup, let’s write some integration tests for the S3CrudService operations.
在完成基础架构设置后,让我们为 S3CrudService 操作编写一些集成测试。
First, let’s create a bucket and verify its successful creation:
首先,让我们创建一个水桶并验证其创建是否成功:
var s3CrudService = new S3CrudService(s3Client);
s3CrudService.createBucket(TEST_BUCKET_NAME);
var createdBucketName = s3Client.listBuckets().buckets().get(0).name();
assertThat(TEST_BUCKET_NAME).isEqualTo(createdBucketName);
After successfully creating the bucket, let’s upload a new object in S3.
成功创建桶后,让我们在 S3 中上传一个新对象。
To do so, first, we generate an array of bytes using FileGenerator, and then the createObject() method saves it as an object in the already created bucket:
为此,我们首先使用 FileGenerator 生成一个字节数组,然后使用 createObject() 方法将其作为一个对象保存到已创建的数据桶中:
var fileToSave = FileGenerator.generateFiles(1, 100).get(0);
s3CrudService.createObject(TEST_BUCKET_NAME, fileToSave);
Next, let’s call the getObject() method with the file name of the already saved file to confirm if the object was indeed saved in S3:
接下来,让我们使用已保存文件的文件名调用 getObject() 方法,以确认对象是否确实保存在 S3 中:
var savedFileContent = s3CrudService.getObject(TEST_BUCKET_NAME, fileToSave.getName());
assertThat(Arrays.equals(fileToSave.getContent().array(), savedFileContent)).isTrue();
Finally, let’s test that the deleteObject() also works as expected. To begin with, we call the deleteObject() method with the bucket name and the targeted filename. Subsequently, we call again the getObject() and check that the result is empty:
最后,让我们测试一下 deleteObject() 是否也能按预期运行。首先,我们使用数据桶名称和目标文件名调用 deleteObject() 方法。随后,我们再次调用 getObject() 并检查结果是否为空:
s3CrudService.deleteObject(TEST_BUCKET_NAME,fileToSave.getName());
var deletedFileContent = s3CrudService.getObject(TEST_BUCKET_NAME, fileToSave.getName());
assertThat(deletedFileContent).isEmpty();
5. Conclusion
5.结论
In this tutorial, we learned how to write integration tests that depend on the AWS S3 service by using the S3Mock library to mock a real S3 service.
在本教程中,我们学习了如何通过使用 S3Mock 库模拟真实 S3 服务来编写依赖于 AWS S3 服务的集成测试。
To demonstrate this, first, we implemented a basic CRUD service that creates, reads, and deletes objects from S3. Then, we implemented the integration tests using the S3Mock library.
为了演示这一点,我们首先实现了一个基本的 CRUD 服务,可以从 S3 创建、读取和删除对象。然后,我们使用 S3Mock 库实施集成测试。
As always, the full implementation of this article can be found over on GitHub.