用于 JUnit 测试的高效对象存储

有一天,我遇到了从PostgreSQL下载一个相对较大的二进制数据文件的问题。存储和获取此类数据有几个限制(所有限制都可以在官方文档中找到)。为了解决这个问题,建议找到更合适的数据存储。

出于某些内部原因,为此目的选择了众所周知的 Amazon S3 存储桶。该选择影响了项目的单元测试基础。仍然无法继续使用 HSQL 或 H2 等轻量级数据库来实现测试。这是我们将在本文中尝试解决的一个关键问题。

对象存储构建
保持单元测试活跃的一种可能的解决方案是实现一些模拟对象存储,与 S3 存储桶客户端完全兼容,另一方面,我们可以使用这种类型的现有对象存储。MinIO 是一个非常简单但高性能的对象存储的一个很好的例子,它同时与 Amazon S3 兼容(至少在文档中是这样写的)。

为了将 MinIO 集成到我们的单元测试中,我们将使用一个用 Java 编写的强大的 Testcontainers 库。Testcontainers 是一个特殊的库,它支持 JUnit 测试,并提供通用数据库、Selenium Web 浏览器以及可以在 Docker 容器中运行的任何其他内容的轻量级一次性实例。要开始使用这个惊人的库,只需要拥有 Docker 并将以下依赖项添加到我们的pom.xml:

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>testcontainers</artifactId>
    <version>1.12.3</version>
    <scope>test</scope>
</dependency>

不幸的是,没有适合我们目标的容器,但该库提供了所有必要的工具,可以自己轻松创建它。幸运的是,DockerHub 上有一个 MinIO 的官方 docker 镜像。

要创建自己的 MinIO 容器,必须使用自定义数据进行扩展: (MonIO 文档建议使用 9000 端口)、 (映像名称)、 (映像版本)。GenericContainerDEFAULT_PORTDEFAULT_IMAGEDEFAULT_TAG

注意标签分配!在我们的示例中,它使用“edge”标签来支持上次部署的 MinIO 版本,但大多数时候最好不时修复标签并手动更新它,以避免不可预测的测试崩溃。还强烈建议提供凭据(访问密钥、私有密钥)来控制对容器的访问。下面是自定义 MinIO 容器的实现示例:

public class MinioContainer extends GenericContainer<MinioContainer> {
    private static final int DEFAULT_PORT = 9000;
    private static final String DEFAULT_IMAGE = "minio/minio";
    private static final String DEFAULT_TAG = "edge";
    private static final String MINIO_ACCESS_KEY = "MINIO_ACCESS_KEY";
    private static final String MINIO_SECRET_KEY = "MINIO_SECRET_KEY";
    private static final String DEFAULT_STORAGE_DIRECTORY = "/data";
    private static final String HEALTH_ENDPOINT = "/minio/health/ready";
    public MinioContainer(CredentialsProvider credentials) {
        this(DEFAULT_IMAGE + ":" + DEFAULT_TAG, credentials);
    }
    public MinioContainer(String image, CredentialsProvider credentials) {
        super(image == null ? DEFAULT_IMAGE + ":" + DEFAULT_TAG : image);
        withNetworkAliases("minio-" + Base58.randomString(6));
        addExposedPort(DEFAULT_PORT);
        if (credentials != null) {
            withEnv(MINIO_ACCESS_KEY, credentials.getAccessKey());
            withEnv(MINIO_SECRET_KEY, credentials.getSecretKey());
        }
        withCommand("server", DEFAULT_STORAGE_DIRECTORY);
        setWaitStrategy(new HttpWaitStrategy()
                .forPort(DEFAULT_PORT)
                .forPath(HEALTH_ENDPOINT)
                .withStartupTimeout(Duration.ofMinutes(2)));
    }
    public String getHostAddress() {
        return getContainerIpAddress() + ":" + getMappedPort(DEFAULT_PORT);
    }
    public static class CredentialsProvider {
        private String accessKey;
        private String secretKey;
        public CredentialsProvider(String accessKey, String secretKey) {
            this.accessKey = accessKey;
            this.secretKey = secretKey;
        }
        // getters
    }
}

测试
由于我们有一个合适的测试容器,可以用作 Amazon S3 存储桶对象存储的提供程序,因此是时候展示一个简单的 JUnit 测试示例了。当然,首先我们需要配置 S3 客户端以与我们的容器进行交互。在我们的例子中,我们使用原始的 AmazonS3Client。因此,为了实现我们的单元测试,我们需要添加一个额外的依赖项。

<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-java-sdk-s3</artifactId>
    <version>1.11.60</version>
</dependency>
<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-java-sdk</artifactId>
    <version>1.11.60</version>
</dependency>

以下是创建具有指定名称的 S3 存储桶的普通测试:


public class MinioContainerTest {
    private static final String ACCESS_KEY = "accessKey";
    private static final String SECRET_KEY = "secretKey";
    private static final String BUCKET = "bucket";
    private AmazonS3Client client = null;
    @After
    public void shutDown() {
        if (client != null) {
            client.shutdown();
            client = null;
        }
    }
    @Test
    public void testCreateBucket() {
        try (MinioContainer container = new MinioContainer(
                new MinioContainer.CredentialsProvider(ACCESS_KEY, SECRET_KEY))) {
            container.start();
            client = getClient(container);
            Bucket bucket = client.createBucket(BUCKET);
            assertNotNull(bucket);
            assertEquals(BUCKET, bucket.getName());
            List<Bucket> buckets = client.listBuckets();
            assertNotNull(buckets);
            assertEquals(1, buckets.size());
            assertTrue(buckets.stream()
                    .map(Bucket::getName)
                    .collect(Collectors.toList())
                    .contains(BUCKET));
        }
    }
    private AmazonS3Client getClient(MinioContainer container) {
        S3ClientOptions clientOptions = S3ClientOptions
                .builder()
                .setPathStyleAccess(true)
                .build();
        client = new AmazonS3Client(new AWSStaticCredentialsProvider(
                new BasicAWSCredentials(ACCESS_KEY, SECRET_KEY)));
        client.setEndpoint("http://" + container.getHostAddress());
        client.setS3ClientOptions(clientOptions);
        return client;
    }
}
  • 50
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小徐博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值