Integration Testing with a Local DynamoDB Instance – 用本地DynamoDB实例进行集成测试

最后修改: 2018年 4月 2日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

If we develop an application which uses Amazon’s DynamoDB, it can be tricky to develop integration tests without having a local instance.

如果我们开发的应用程序使用 Amazon 的 DynamoDB,那么在没有本地实例的情况下,开发集成测试就会很棘手。

In this tutorial, we’ll explore multiple ways of configuring, starting and stopping a local DynamoDB for our integration tests.

在本教程中,我们将探索为我们的集成测试配置、启动和停止本地DynamoDB的多种方法

This tutorial also complements our existing DynamoDB article.

本教程还补充了我们现有的DynamoDB文章

2. Configuration

2.配置

2.1. Maven Setup

2.1.Maven设置

DynamoDB Local is a tool developed by Amazon which supports all the DynamoDB APIs. It doesn’t directly manipulate the actual DynamoDB tables in production but performs it locally instead.

DynamoDB Local是一个由Amazon开发的工具,它支持所有DynamoDB API。它不直接操作生产中的实际DynamoDB表,而是在本地进行操作。

First, we add the DynamoDB Local dependency to the list of dependencies in our Maven configuration:

首先,我们将DynamoDB本地依赖关系添加到Maven配置的依赖关系列表中。

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

Next, we also need to add the Amazon DynamoDB repository, since the dependency doesn’t exist in the Maven Central repository.

接下来,我们还需要添加Amazon DynamoDB资源库,因为Maven Central资源库中不存在该依赖项。

We can select the closest Amazon server to our current IP address geolocation:

我们可以选择离我们当前IP地址地理位置最近的亚马逊服务器。

<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. Add SQLite4Java Dependency

2.2.添加SQLite4Java依赖项

The DynamoDB Local uses the SQLite4Java library internally; thus, we also need to include the library files when we run the test. The SQLite4Java library files depend on the environment where the test is running, but Maven can pull them transitively once we declare the DynamoDBLocal dependency.

DynamoDB Local内部使用SQLite4Java库;因此,我们在运行测试时也需要包含该库文件。SQLite4Java库文件取决于测试运行的环境,但一旦我们声明了DynamoDBLocal的依赖关系,Maven就可以将其转发。

Next, we need to add a new build step to copy native libraries into a specific folder that we’ll define in the JVM system property later on.

接下来,我们需要添加一个新的构建步骤,将本地库复制到我们稍后将在JVM系统属性中定义的特定文件夹。

Let’s copy the transitively-pulled SQLite4Java library files to a folder named native-libs:

让我们把过境拉来的SQLite4Java库文件复制到一个名为native-libs的文件夹。

<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. Set the SQLite4Java System Property

2.3.设置SQLite4Java的系统属性

Now, we’ll reference the previously created folder (where the SQLite4Java libraries are located), using a JVM system property named sqlite4java.library.path:

现在,我们将使用一个名为sqlite4java.library.path的JVM系统属性来引用之前创建的文件夹(SQLite4Java库的位置)。

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

In order to successfully run the test later, it’s mandatory to have all the SQLite4Java libraries in the folder defined by the sqlite4java.library.path system property. We must run Maven test-compile (mvn test-compile) at least once to fulfill the prerequisite.

为了以后成功运行测试,必须在sqlite4java.library.path系统属性定义的文件夹中拥有所有SQLite4Java库。我们必须至少运行一次Maven test-compile(mvn test-compile)来满足前提条件

3. Setting up the Test Database’s Lifecycle

3.设置测试数据库的生命周期

We can define the code to create and start the local DynamoDB server in a setup method annotated with @BeforeClass; and, symmetrically, stop the server in a teardown method annotated with @AfterClass.

我们可以在一个用@BeforeClass;注解的设置方法中定义创建和启动本地DynamoDB服务器的代码,并对称地在一个用@AfterClass注解的拆分方法中停止服务器。

In the following example, we’ll start up the local DynamoDB server on port 8000 and make sure it’s stopped again after running our tests:

在下面的例子中,我们将在8000端口启动本地DynamoDB服务器,并确保它在运行我们的测试后再次停止。

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();
    }

    //...
}

We can also run the local DynamoDB server on any available port instead of a fixed port using java.net.ServerSocket. In this case, we must also configure the test to set the endpoint to the correct DynamoDB port:

我们也可以使用java.net.ServerSocket在任何可用的端口而不是固定的端口上运行本地DynamoDB服务器。在这种情况下,我们还必须对测试进行配置,将端点设置为正确的DynamoDB端口

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

4. Alternative Approach: Using @ClassRule

4.替代方法 使用@ClassRule

We can wrap the previous logic in a JUnit rule which performs the same action:

我们可以把前面的逻辑包在一个JUnit规则中,执行同样的动作。

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);
        }    
    }
}

To use our custom rule, we’ll have to create and annotate an instance with @ClassRule as shown below. Again, the test will create and start the local DynamoDB server prior to the test class initialization.

为了使用我们的自定义规则,我们必须创建一个带有@ClassRule的实例并加以注释,如下所示。同样,测试将在测试类初始化之前创建并启动本地DynamoDB服务器。

Note that the access modifier of the test rule must be public in order to run the test:

注意,测试规则的访问修饰语必须是public ,以便运行测试。

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

    //...
}

 

Before wrapping up, a very quick note – since DynamoDB Local uses the SQLite database internally, its performance doesn’t reflect the real performance in production.

在结束之前,有一个非常简短的说明–由于DynamoDB Local内部使用SQLite数据库,其性能并不能反映生产中的真实性能。

5. Conclusion

5.结论

In this article, we’ve seen how to setup and configure DynamoDB Local to run integration tests.

在这篇文章中,我们已经看到了如何设置和配置DynamoDB Local来运行集成测试。

As always, the source code and the configuration example can be found over on Github.

一如既往,源代码和配置示例可以在Github上找到over