如何Mock Amazon S3进行集成测试

2025/03/19

1. 简介

在本文中,我们将学习如何Mock Amazon S3(简单存储服务)来运行Java应用程序的集成测试。

为了演示其工作原理,我们将创建一个使用AWS SDK与S3交互的CRUD服务。然后,我们将使用Mock的S3服务为每个操作编写集成测试。

2. S3概述

Amazon Simple Storage Service(S3)是AWS提供的一种高度可扩展且安全的云存储服务,它使用对象存储模型,允许用户在网络上的任何位置存储和检索数据

该服务可通过REST风格的API访问,AWS为Java应用程序提供了SDK来执行创建、列出和删除S3存储桶和对象等操作。

接下来,我们开始使用AWS SDK为S3创建Java CRUD服务,并实现创建、读取、更新和删除操作。

3. 演示S3 CRUD Java服务

在开始使用S3之前,我们需要在项目中添加对AWS SDK的依赖:

<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>s3</artifactId>
    <version>2.20.52</version>
</dependency>

要查看最新版本,可以检查中央仓库

接下来,我们创建S3CrudService类,并以software.amazon.awssdk.services.s3.S3Client作为依赖项:

class S3CrudService {
    private final S3Client s3Client;

    public S3CrudService(S3Client s3Client) {
        this.s3Client = s3Client;
    }

    // ...
}

现在我们已经创建了Service,让我们使用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操作,让我们学习如何使用Mock S3服务来实现集成测试。

4. 使用S3Mock库进行集成测试

在本教程中,我们选择使用Adobe开源的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上的s3mocks3mock-testcontainersjunit-jupiter链接来查看最新版本。

4.2 设置

作为先决条件,我们必须有一个正在运行的Docker环境以确保可以启动测试容器

当我们在集成测试类上使用@TestConainers和@Container注解时,S3MockContainer的最新Docker镜像将从注册表中提取并在本地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接口提供的构建器初始化了S3Client的实例。在此初始化过程中,我们为以下参数指定了配置:

  • endOverwrite:此参数配置用于定义S3 Mock服务的地址
  • 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库Mock真实的S3服务来编写依赖于AWS S3服务的集成测试

为了演示这一点,我们首先实现了一个基本的CRUD服务,用于从S3创建、读取和删除对象。然后,我们使用S3Mock库实现了集成测试。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章