1. 概述

当开发使用 Amazon DynamoDB 的应用时,没有本地实例进行集成测试会是个麻烦事。本文将探讨多种配置、启动和停止本地DynamoDB用于集成测试的方法

本文是对现有 DynamoDB 文章 的补充。

2. 配置

2.1 Maven 设置

DynamoDB Local 是亚马逊开发的工具,支持所有 DynamoDB API。它不会直接操作生产环境的 DynamoDB 表,而是在本地执行。

首先,在 Maven 配置中添加 DynamoDB Local 依赖:

<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>DynamoDBLocal</artifactId>
    <version>1.11.86</version>
    <scope>test</scope>
</dependency>

接下来需要添加 Amazon DynamoDB 仓库,因为该依赖不在 Maven 中央仓库:

<repository>
    <id>dynamodb-local</id>
    <name>DynamoDB Local Release Repository</name>
    <url>https://s3-us-west-2.amazonaws.com/dynamodb-local/release</url>
</repository>

2.2 添加 SQLite4Java 依赖

DynamoDB Local 内部使用 SQLite4Java 库,因此运行测试时需要包含该库文件。SQLite4Java 库文件依赖测试运行环境,但声明 DynamoDBLocal 依赖后 Maven 会自动拉取。

添加构建步骤将原生库复制到指定文件夹(后续通过 JVM 系统属性引用):

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <version>2.10</version>
    <executions>
        <execution>
            <id>copy</id>
            <phase>test-compile</phase>
            <goals>
                <goal>copy-dependencies</goal>
            </goals>
            <configuration>
                <includeScope>test</includeScope>
                <includeTypes>so,dll,dylib</includeTypes>
                <outputDirectory>${project.basedir}/native-libs</outputDirectory>
            </configuration>
        </execution>
    </executions>
</plugin>

2.3 设置 SQLite4Java 系统属性

通过 JVM 系统属性 sqlite4java.library.path 引用 SQLite4Java 库所在文件夹:

System.setProperty("sqlite4java.library.path", "native-libs");

⚠️ 关键前提:必须确保 sqlite4java.library.path 指定的文件夹包含所有 SQLite4Java 库文件。至少执行一次 mvn test-compile 来满足此条件。

3. 管理测试数据库生命周期

@BeforeClass 注解的 setup 方法中创建并启动本地 DynamoDB 服务器,在 @AfterClass 注解的 teardown 方法中停止服务器:

public class ProductInfoDAOIntegrationTest {
    private static DynamoDBProxyServer server;

    @BeforeClass
    public static void setupClass() throws Exception {
        System.setProperty("sqlite4java.library.path", "native-libs");
        String port = "8000";
        server = ServerRunner.createServerFromCommandLineArgs(
          new String[]{"-inMemory", "-port", port});
        server.start();
        //...
    }

    @AfterClass
    public static void teardownClass() throws Exception {
        server.stop();
    }

    //...
}

动态端口技巧:使用 java.net.ServerSocket 获取可用端口替代固定端口:

public String getAvailablePort() throws IOException {
    ServerSocket serverSocket = new ServerSocket(0);
    return String.valueOf(serverSocket.getLocalPort());
}

此时必须配置测试使用正确的 DynamoDB 接口

4. 替代方案:使用 @ClassRule

将上述逻辑封装为 JUnit Rule:

public class LocalDbCreationRule extends ExternalResource {
    private DynamoDBProxyServer server;

    public LocalDbCreationRule() {
        System.setProperty("sqlite4java.library.path", "native-libs");
    }

    @Override
    protected void before() throws Exception {
        String port = "8000";
        server = ServerRunner.createServerFromCommandLineArgs(
          new String[]{"-inMemory", "-port", port});
        server.start();
    }

    @Override
    protected void after() {
        this.stopUnchecked(server);
    }

    protected void stopUnchecked(DynamoDBProxyServer dynamoDbServer) {
        try {
            dynamoDbServer.stop();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }    
    }
}

使用 @ClassRule 注解规则实例(注意访问修饰符必须为 public):

public class ProductInfoRepositoryIntegrationTest {
    @ClassRule
    public static LocalDbCreationRule dynamoDB = new LocalDbCreationRule();

    //...
}

⚠️ 性能提醒:DynamoDB Local 内部使用 SQLite 数据库,其性能不反映生产环境真实表现

5. 总结

本文介绍了配置和使用 DynamoDB Local 进行集成测试的多种方法。完整源码和配置示例可在 GitHub 获取。


原始标题:Integration Testing with a Local DynamoDB Instance