简体   繁体   English

更轻松的 DynamoDB 本地测试

[英]Easier DynamoDB local testing

I'm using DynamoDB local for unit testing.我正在使用本地 DynamoDB进行单元测试。 It's not bad, but has some drawbacks.这还不错,但有一些缺点。 Specifically:具体来说:

  • You have to somehow start the server before your tests run在测试运行之前,您必须以某种方式启动服务器
  • The server isn't started and stopped before each test so tests become inter-dependent unless you add code to delete all tables, etc. after each test服务器不会在每次测试之前启动和停止,因此测试变得相互依赖,除非您在每次测试后添加代码以删除所有表等
  • All developers need to have it installed所有开发人员都需要安装它

What I want to do is something like put the DynamoDB local jar, and the other jars upon which it depends, in my test/resources directory (I'm writing in Java).我想要做的是将 DynamoDB 本地 jar 和它所依赖的另一个 jars 放在我的test/resources目录中(我正在用 Java 编写)。 Then before each test I'd start it up, running with -inMemory , and after the test I'd stop it.然后在每次测试之前我会启动它,使用-inMemory运行,在测试之后我会停止它。 That way anyone pulling down the git repo gets a copy of everything they need to run the tests and each test is independent of the others.这样一来,任何下载 git 存储库的人都可以获得运行测试所需的所有内容的副本,并且每个测试都独立于其他测试。

I have found a way to make this work, but it's ugly, so I'm looking for alternatives.我找到了一种方法来完成这项工作,但它很丑陋,所以我正在寻找替代方案。 The solution I have is to put a.zip file of the DynamoDB local stuff in test/resources , then in the @Before method, I'd extract it to some temporary directory and start a new java process to execute it.我的解决方案是将 DynamoDB 本地文件的 .zip 文件放在test/resources中,然后在@Before方法中,我将其解压缩到某个临时目录并启动一个新的 java 进程来执行它。 That works, but it's ugly and has some drawbacks:这行得通,但它很丑陋并且有一些缺点:

  • Everyone needs the java executable on their $PATH每个人的$PATH中都需要 java 可执行文件
  • I have to unpack a zip to the local disk.我必须解压一个 zip 到本地磁盘。 Using local disk is often dicey for testing, especially with continuous builds and such.使用本地磁盘进行测试通常很冒险,尤其是在连续构建等情况下。
  • I have to spawn a process and wait for it to start for each unit test, and then kill that process after each test.我必须生成一个进程并等待它为每个单元测试启动,然后在每次测试后终止该进程。 Besides being slow, the potential for left-over processes seems ugly.除了速度慢之外,遗留流程的可能性似乎也很难看。

It seems like there should be an easier way.似乎应该有一个更简单的方法。 DynamoDB Local is, after all, just Java code.毕竟,DynamoDB Local 只是 Java 代码。 Can't I somehow ask the JVM to fork itself and look inside the resources to build a classpath?我不能以某种方式要求 JVM 分叉自身并查看资源以构建类路径吗? Or, even better, can't I just call the main method of DynamoDB Local from some other thread so this all happens in a single process?或者,更好的是,我不能只从其他线程调用 DynamoDB Local 的main方法,这样这一切都发生在一个进程中吗? Any ideas?有任何想法吗?

PS: I am aware of Alternator, but it appears to have other drawbacks so I'm inclined to stick with Amazon's supported solution if I can make it work. PS:我知道 Alternator,但它似乎还有其他缺点,所以如果我能让它工作,我倾向于坚持使用 Amazon 支持的解决方案。

In order to use DynamoDBLocal you need to follow these steps.要使用 DynamoDBLocal,您需要执行以下步骤。

  1. Get Direct DynamoDBLocal Dependency获取直接的 DynamoDBLocal 依赖
  2. Get Native SQLite4Java dependencies获取本机 SQLite4Java 依赖项
  3. Set sqlite4java.library.path to show native libraries设置sqlite4java.library.path以显示本机库

1. Get Direct DynamoDBLocal Dependency 1. 获取直接的 DynamoDBLocal 依赖

This one is the easy one.这是最简单的一种。 You need this repository as explained here .为解释你需要这个仓库在这里

<!--Dependency:-->
<dependencies>
    <dependency>
        <groupId>com.amazonaws</groupId>
        <artifactId>DynamoDBLocal</artifactId>
        <version>1.11.0.1</version>
        <scope></scope>
    </dependency>
</dependencies>
<!--Custom repository:-->
<repositories>
    <repository>
        <id>dynamodb-local</id>
        <name>DynamoDB Local Release Repository</name>
        <url>https://s3-us-west-2.amazonaws.com/dynamodb-local/release</url>
    </repository>
</repositories>

2. Get Native SQLite4Java dependencies 2. 获取 Native SQLite4Java 依赖

If you do not add these dependencies, your tests will fail with 500 internal error.如果您不添加这些依赖项,您的测试将失败并显示 500 内部错误。

First, add these dependencies:首先,添加这些依赖项:

<dependency>
    <groupId>com.almworks.sqlite4java</groupId>
    <artifactId>sqlite4java</artifactId>
    <version>1.0.392</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>com.almworks.sqlite4java</groupId>
    <artifactId>sqlite4java-win32-x86</artifactId>
    <version>1.0.392</version>
    <type>dll</type>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>com.almworks.sqlite4java</groupId>
    <artifactId>sqlite4java-win32-x64</artifactId>
    <version>1.0.392</version>
    <type>dll</type>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>com.almworks.sqlite4java</groupId>
    <artifactId>libsqlite4java-osx</artifactId>
    <version>1.0.392</version>
    <type>dylib</type>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>com.almworks.sqlite4java</groupId>
    <artifactId>libsqlite4java-linux-i386</artifactId>
    <version>1.0.392</version>
    <type>so</type>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>com.almworks.sqlite4java</groupId>
    <artifactId>libsqlite4java-linux-amd64</artifactId>
    <version>1.0.392</version>
    <type>so</type>
    <scope>test</scope>
</dependency>

Then, add this plugin to get native dependencies to specific folder:然后,添加此插件以获取特定文件夹的本机依赖项:

<build>
    <plugins>
        <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>
    </plugins>
</build>

3. Set sqlite4java.library.path to show native libraries 3.设置sqlite4java.library.path显示原生库

As last step, you need to set sqlite4java.library.path system property to native-libs directory.作为最后一步,您需要将sqlite4java.library.path系统属性设置为 native-libs 目录。 It is OK to do that just before creating your local server.在创建本地服务器之前这样做是可以的。

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

After these steps you can use DynamoDBLocal as you want.完成这些步骤后,您可以根据需要使用 DynamoDBLocal。 Here is a Junit rule that creates local server for that.这是为此创建本地服务器的 Junit 规则。

import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
import com.amazonaws.services.dynamodbv2.local.main.ServerRunner;
import com.amazonaws.services.dynamodbv2.local.server.DynamoDBProxyServer;
import org.junit.rules.ExternalResource;

import java.io.IOException;
import java.net.ServerSocket;

/**
 * Creates a local DynamoDB instance for testing.
 */
public class LocalDynamoDBCreationRule extends ExternalResource {

    private DynamoDBProxyServer server;
    private AmazonDynamoDB amazonDynamoDB;

    public LocalDynamoDBCreationRule() {
        // This one should be copied during test-compile time. If project's basedir does not contains a folder
        // named 'native-libs' please try '$ mvn clean install' from command line first
        System.setProperty("sqlite4java.library.path", "native-libs");
    }

    @Override
    protected void before() throws Throwable {

        try {
            final String port = getAvailablePort();
            this.server = ServerRunner.createServerFromCommandLineArgs(new String[]{"-inMemory", "-port", port});
            server.start();
            amazonDynamoDB = new AmazonDynamoDBClient(new BasicAWSCredentials("access", "secret"));
            amazonDynamoDB.setEndpoint("http://localhost:" + port);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void after() {

        if (server == null) {
            return;
        }

        try {
            server.stop();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public AmazonDynamoDB getAmazonDynamoDB() {
        return amazonDynamoDB;
    }

    private String getAvailablePort() {
        try (final ServerSocket serverSocket = new ServerSocket(0)) {
            return String.valueOf(serverSocket.getLocalPort());
        } catch (IOException e) {
            throw new RuntimeException("Available port was not found", e);
        }
    }
}

You can use this rule like this你可以像这样使用这个规则

@RunWith(JUnit4.class)
public class UserDAOImplTest {

    @ClassRule
    public static final LocalDynamoDBCreationRule dynamoDB = new LocalDynamoDBCreationRule();
}

You can use DynamoDB Local as a Maven test dependency in your test code, as is shown in this announcement .您可以在测试代码中使用 DynamoDB Local 作为 Maven 测试依赖项,如本公告中所示。 You can run over HTTP:您可以通过 HTTP 运行:

import com.amazonaws.services.dynamodbv2.local.main.ServerRunner;
import com.amazonaws.services.dynamodbv2.local.server.DynamoDBProxyServer;

final String[] localArgs = { "-inMemory" };
DynamoDBProxyServer server = ServerRunner.createServerFromCommandLineArgs(localArgs);
server.start();
AmazonDynamoDB dynamodb = new AmazonDynamoDBClient();
dynamodb.setEndpoint("http://localhost:8000");
dynamodb.listTables();
server.stop();

You can also run in embedded mode:您还可以在嵌入式模式下运行:

import com.amazonaws.services.dynamodbv2.local.embedded.DynamoDBEmbedded;

AmazonDynamoDB dynamodb = DynamoDBEmbedded.create();
dynamodb.listTables();

This is a restating of bhdrkn's answer for Gradle users (his is based on Maven).这是 bhdrkn 对 Gradle 用户的回答的重述(他的回答基于 Maven)。 It's still the same three steps:还是一样的三步:

  1. Get Direct DynamoDBLocal Dependency获取直接的 DynamoDBLocal 依赖
  2. Get Native SQLite4Java dependencies获取本机 SQLite4Java 依赖项
  3. Set sqlite4java.library.path to show native libraries设置 sqlite4java.library.path 以显示本机库

1. Get Direct DynamoDBLocal Dependency 1. 获取直接的 DynamoDBLocal 依赖

Add to the dependencies section of your build.gradle file...添加到 build.gradle 文件的依赖项部分...

dependencies {
    testCompile "com.amazonaws:DynamoDBLocal:1.+"
}

2. Get Native SQLite4Java dependencies 2. 获取 Native SQLite4Java 依赖

The sqlite4java libraries will already be downloaded as a dependency of DynamoDBLocal, but the library files need to be copied to the right place. sqlite4java 库已作为 DynamoDBLocal 的依赖项下载,但需要将库文件复制到正确的位置。 Add to your build.gradle file...添加到您的 build.gradle 文件...

task copyNativeDeps(type: Copy) {
    from(configurations.compile + configurations.testCompile) {
        include '*.dll'
        include '*.dylib'
        include '*.so'
    }
    into 'build/libs'
}

3. Set sqlite4java.library.path to show native libraries 3.设置sqlite4java.library.path显示原生库

We need to tell Gradle to run copyNativeDeps for testing and tell sqlite4java where to find the files.我们需要告诉 Gradle 运行copyNativeDeps进行测试,并告诉 sqlite4java 在哪里可以找到这些文件。 Add to your build.gradle file...添加到您的 build.gradle 文件...

test {
    dependsOn copyNativeDeps
    systemProperty "java.library.path", 'build/libs'
}

In August 2018 Amazon announced new Docker image with Amazon DynamoDB Local onboard. 2018 年 8 月, 亚马逊发布了带有 Amazon DynamoDB Local 的新Docker 镜像 It does not require downloading and running any JARs as well as adding using third-party OS-specific binaries (I'm talking about sqlite4java ).它不需要下载和运行任何 JAR,也不需要使用第三方操作系统特定的二进制文件(我说的是sqlite4java )。

It is as simple as starting a Docker container before the tests:它就像在测试之前启动一个 Docker 容器一样简单:

docker run -p 8000:8000 amazon/dynamodb-local

You can do that manually for local development, as described above, or use it in your CI pipeline.如上所述,您可以为本地开发手动执行此操作,也可以在 CI 管道中使用它。 Many CI services provide an ability to start additional containers during the pipeline that can provide dependencies for your tests.许多 CI 服务提供在管道期间启动额外容器的能力,这些容器可以为您的测试提供依赖项。 Here is an example for Gitlab CI/CD:以下是 Gitlab CI/CD 的示例:

test:
  stage: test
  image: openjdk:8-alpine
  services:
    - name: amazon/dynamodb-local
      alias: dynamodb-local
  script:
    - DYNAMODB_LOCAL_URL=http://dynamodb-local:8000 ./gradlew clean test

Or Bitbucket Pipelines:或 Bitbucket 管道:

definitions:
  services:
    dynamodb-local:
      image: amazon/dynamodb-local
…
step:
  name: test
  image:
    name: openjdk:8-alpine
  services:
    - dynamodb-local
  script:
    - DYNAMODB_LOCAL_URL=http://localhost:8000 ./gradlew clean test

And so on.等等。 The idea is to move all the configuration you can see in other answers out of your build tool and provide the dependency externally.这个想法是将您在其他答案中可以看到的所有配置移出构建工具,并在外部提供依赖项。 Think of it as of dependency injection / IoC but for the whole service, not just a single bean.将其视为依赖注入/IoC,但对于整个服务,而不仅仅是单个 bean。

After you've started the container you can create a client pointing to it:启动容器后,您可以创建一个指向它的客户端:

private AmazonDynamoDB createAmazonDynamoDB(final DynamoDBLocal configuration) {
    return AmazonDynamoDBClientBuilder
        .standard()
        .withEndpointConfiguration(
            new AwsClientBuilder.EndpointConfiguration(
                "http://localhost:8000",
                Regions.US_EAST_1.getName()
            )
        )
        .withCredentials(
            new AWSStaticCredentialsProvider(
                // DynamoDB Local works with any non-null credentials
                new BasicAWSCredentials("", "")
            )
        )
        .build();
}

Now to the original questions:现在回到最初的问题:

You have to somehow start the server before your tests run您必须在测试运行之前以某种方式启动服务器

You can just start it manually, or prepare a developsers' script for it.您可以手动启动它,或者为它准备一个开发人员的脚本。 IDEs usually provide a way to run arbitrary commands before executing a task, so you can make IDE to start the container for you. IDE 通常提供一种在执行任务之前运行任意命令的方法,因此您可以让 IDE为您启动容器。 I think that running something locally should not be a top priority in this case, but instead you should focus on configuring CI and let the developers start the container as it's comfortable to them.我认为在这种情况下,在本地运行某些东西不应该是重中之重,相反,您应该专注于配置 CI,让开发人员启动容器,因为它对他们来说很舒服。

The server isn't started and stopped before each test so tests become inter-dependent unless you add code to delete all tables, etc. after each test服务器不会在每次测试之前启动和停止,因此测试变得相互依赖,除非您在每次测试后添加代码以删除所有表等

That's trueee, but… You should not start and stop such heavyweight things and recreate tables before / after each test.没错,但是……您不应该在每次测试之前/之后开始和停止此类重量级的事情并重新创建表。 DB tests are almost always inter-dependent and that's ok for them.数据库测试几乎总是相互依赖的,这对他们来说没问题。 Just use unique values for each test case (eg set item's hash key to ticket id / specific test case id you're working on).只需为每个测试用例使用唯一的值(例如,将项目的哈希键设置为您正在处理的票证 ID/特定测试用例 ID)。 As for the seed data, I'd recommend moving it from the build tool and test code as well.至于种子数据,我建议也将其从构建工具和测试代码中移出。 Either make your own image with all the data you need or use AWS CLI to create tables and insert data.要么使用您需要的所有数据制作您自己的图像,要么使用 AWS CLI 创建表并插入数据。 Follow the single responsibility principle and dependency injection principles: your test code must not do anything but tests.遵循单一职责原则和依赖注入原则:你的测试代码除了测试什么都不做。 All the environment (tables and data in this case should be provided for them).所有环境(在这种情况下应为它们提供表和数据)。 Creating a table in a test is wrong, because in a real life that table already exist (unless you're testing a method that actually creates a table, of course).在测试中创建表是错误的,因为在现实生活中该表已经存在(当然,除非您正在测试实际创建表的方法)。

All developers need to have it installed所有开发人员都需要安装它

Docker should be a must for every developer in 2018, so that's not a problem. Docker 应该是 2018 年每个开发者的必备品,所以这不是问题。


And if you're using JUnit 5, it can be a good idea to use a DynamoDB Local extension that will inject the client in your tests (yes, I'm doing a self-promotion):如果您使用的是 JUnit 5,最好使用DynamoDB Local 扩展来在您的测试中注入客户端(是的,我正在做自我宣传):

  1. Add JCenter repository to your build.JCenter存储库添加到您的构建中。

    pom.xml : pom.xml :

     <repositories> <repository> <snapshots> <enabled>false</enabled> </snapshots> <id>central</id> <name>bintray</name> <url>https://jcenter.bintray.com</url> </repository> </repositories>

    build.gradle构建.gradle

     repositories { jcenter() }
  2. Add a dependency on by.dev.madhead.aws-junit5:dynamodb-v1添加对by.dev.madhead.aws-junit5:dynamodb-v1的依赖

    pom.xml : pom.xml :

     <dependency> <groupId>by.dev.madhead.aws-junit5</groupId> <artifactId>dynamodb-v1</artifactId> <version>1.0.0</version> <scope>test</scope> </dependency>

    build.gradle构建.gradle

     dependencies { testImplementation("by.dev.madhead.aws-junit5:dynamodb-v1:1.0.0") }
  3. Use the extension in your tests:在您的测试中使用扩展:

     @ExtendWith(DynamoDBLocalExtension.class) class MultipleInjectionsTest { @DynamoDBLocal( url = "http://dynamodb-local-1:8000" ) private AmazonDynamoDB first; @DynamoDBLocal( urlEnvironmentVariable = "DYNAMODB_LOCAL_URL" ) private AmazonDynamoDB second; @Test void test() { first.listTables(); second.listTables(); } }

I have wrapped the answers above into two JUnit rules that does not require changes to the build script as the rules handles the native library stuff.我将上面的答案包装到两个JUnit 规则中,这些规则不需要更改构建脚本,因为这些规则处理本机库的内容。 I did this as I found that Idea did not like the Gradle/Maven solutions as it just went off and did its own thing anyhoos.我这样做是因为我发现 Idea 不喜欢 Gradle/Maven 解决方案,因为它刚刚开始并做自己的事情。

This means the steps are:这意味着步骤是:

  • Get the AssortmentOfJUnitRules version 1.5.32 or above dependency获取 AssortmentOfJUnitRules 1.5.32 或以上版本的依赖
  • Get the Direct DynamoDBLocal dependency获取直接 DynamoDBLocal 依赖项
  • Add the LocalDynamoDbRule or HttpDynamoDbRule to your JUnit test.将 LocalDynamoDbRule 或 HttpDynamoDbRule 添加到您的 JUnit 测试中。

Maven:马文:

<!--Dependency:-->
<dependencies>
    <dependency>
        <groupId>com.amazonaws</groupId>
        <artifactId>DynamoDBLocal</artifactId>
        <version>1.11.0.1</version>
        <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>com.github.mlk</groupId>
      <artifactId>assortmentofjunitrules</artifactId>
      <version>1.5.36</version>
      <scope>test</scope>
    </dependency>
</dependencies>
<!--Custom repository:-->
<repositories>
    <repository>
        <id>dynamodb-local</id>
        <name>DynamoDB Local Release Repository</name>
        <url>https://s3-us-west-2.amazonaws.com/dynamodb-local/release</url>
    </repository>
</repositories>

Gradle:摇篮:

repositories {
  mavenCentral()

   maven {
    url = "https://s3-us-west-2.amazonaws.com/dynamodb-local/release"
  }
}

dependencies {
    testCompile "com.github.mlk:assortmentofjunitrules:1.5.36"
    testCompile "com.amazonaws:DynamoDBLocal:1.+"
}

Code:代码:

public class LocalDynamoDbRuleTest {
  @Rule
  public LocalDynamoDbRule ddb = new LocalDynamoDbRule();

  @Test
  public void test() {
    doDynamoStuff(ddb.getClient());
  }
}

It seems like there should be an easier way.似乎应该有一个更简单的方法。 DynamoDB Local is, after all, just Java code.毕竟,DynamoDB Local 只是 Java 代码。 Can't I somehow ask the JVM to fork itself and look inside the resources to build a classpath?我不能以某种方式要求 JVM 分叉自身并查看资源以构建类路径吗?

You can do something along these lines, but much simpler: programmatically search the classpath for the location of the native libraries, then set the sqlite4java.library.path property before starting DynamoDB.您可以按照这些思路做一些事情,但要简单得多:以编程方式在类路径中搜索本机库的位置,然后在启动 DynamoDB 之前设置sqlite4java.library.path属性。 This is the approach implemented in tempest-testing , as well as in this answer ( code here) which is why they just work as pure library/classpath dependency and nothing more.这是在tempest-testing以及这个答案代码在这里)中实现的方法,这就是为什么它们只是作为纯库/类路径依赖项工作,仅此而已。

In my case needed access to DynamoDB outside of a JUnit extension, but I still wanted something self-contained in library code, so I extracted the approach it takes:在我的例子中,需要在 JUnit 扩展之外访问 DynamoDB,但我仍然希望在库代码中有一些独立的东西,所以我提取了它采用的方法:

import com.amazonaws.services.dynamodbv2.local.embedded.DynamoDBEmbedded;
import com.amazonaws.services.dynamodbv2.local.shared.access.AmazonDynamoDBLocal;
import com.google.common.collect.MoreCollectors;
import java.io.File;
import java.util.Arrays;
import java.util.stream.Stream;
import org.junit.jupiter.api.condition.OS;

... 

  public AmazonDynamoDBLocal embeddedDynamoDb() {
    final OS os = Stream.of(OS.values()).filter(OS::isCurrentOs)
        .collect(MoreCollectors.onlyElement());
    final String prefix;
    switch (os) {
      case LINUX:
        prefix = "libsqlite4java-linux-amd64-";
        break;
      case MAC:
        prefix = "libsqlite4java-osx-";
        break;
      case WINDOWS:
        prefix = "sqlite4java-win32-x64-";
        break;
      default:
        throw new UnsupportedOperationException(os.toString());
    }
  
    System.setProperty("sqlite4java.library.path",
        Arrays.asList(System.getProperty("java.class.path").split(File.pathSeparator))
            .stream()
            .map(File::new)
            .filter(file -> file.getName().startsWith(prefix))
            .collect(MoreCollectors.onlyElement())
            .getParent());
    return DynamoDBEmbedded.create();
  }

Not had a chance to test on a lot of platforms, and the error handling could likely be improved.没有机会在很多平台上进行测试,错误处理可能会得到改进。

It's a pity AWS haven't taken the time to make the library more friendly, as this could easily be done in the library code itself.遗憾的是 AWS 没有花时间使库更友好,因为这可以在库代码本身中轻松完成。

For unit testing at work I use Mockito, then just mock the AmazonDynamoDBClient.对于工作中的单元测试,我使用 Mockito,然后只模拟 AmazonDynamoDBClient。 then mock out the returns using when.然后使用 when 模拟返回值。 like the following:像下面这样:

when(mockAmazonDynamoDBClient.getItem(isA(GetItemRequest.class))).thenAnswer(new Answer<GetItemResult>() {
        @Override
        public GetItemResult answer(InvocationOnMock invocation) throws Throwable {
            GetItemResult result = new GetItemResult();
            result.setItem( testResultItem );
            return result;
        }
    });

not sure if that is what your looking for but that's how we do it.不确定这是否是您要找的,但我们就是这样做的。

Try out tempest-testing !尝试风暴测试 It ships a JUnit4 Rule and a JUnit5 Extension.它提供了一个 JUnit4 规则和一个 JUnit5 扩展。 It also supports both AWS SDK v1 and SDK v2.它还支持 AWS SDK v1 和 SDK v2。

Tempest provides a library for testing DynamoDB clients using DynamoDBLocal . Tempest 提供了一个使用DynamoDBLocal测试 DynamoDB 客户端的库。 It comes with two implementations:它有两个实现:

  • JVM: This is the preferred option, running a DynamoDBProxyServer backed by sqlite4java , which is available on most platforms . JVM:这是首选选项,运行由DynamoDBProxyServer支持的sqlite4java ,可在大多数平台上使用
  • Docker: This runs dynamodb-local in a Docker container. Docker:它在 Docker 容器中运行dynamodb-local

Feature matrix:特征矩阵:

Feature特征 tempest-testing-jvm暴风雨测试jvm tempest-testing-docker暴风雨测试码头工人
Start up time启动时间 ~1s ~1s ~10s ~10s
Memory usage内存使用情况 Less较少的 More更多的
Dependency依赖 sqlite4java native library sqlite4java 原生库 Docker码头工人

To use tempest-testing , first add this library as a test dependency:要使用tempest-testing ,首先将此库添加为测试依赖项:

For AWS SDK 1.x:对于 AWS 开发工具包 1.x:

dependencies {
  testImplementation "app.cash.tempest:tempest-testing-jvm:1.5.2"
  testImplementation "app.cash.tempest:tempest-testing-junit5:1.5.2"
}
// Or
dependencies {
  testImplementation "app.cash.tempest:tempest-testing-docker:1.5.2"
  testImplementation "app.cash.tempest:tempest-testing-junit5:1.5.2"
}

For AWS SDK 2.x:对于 AWS 开发工具包 2.x:

dependencies {
  testImplementation "app.cash.tempest:tempest2-testing-jvm:1.5.2"
  testImplementation "app.cash.tempest:tempest2-testing-junit5:1.5.2"
}
// Or
dependencies {
  testImplementation "app.cash.tempest:tempest2-testing-docker:1.5.2"
  testImplementation "app.cash.tempest:tempest2-testing-junit5:1.5.2"
}

Then in tests annotated with @org.junit.jupiter.api.Test , you may add TestDynamoDb as a test extension .然后在用@org.junit.jupiter.api.Test注释的测试中,您可以添加TestDynamoDb作为测试扩展 This extension spins up a DynamoDB server.这个扩展启动了一个 DynamoDB 服务器。 It shares the server across tests and keeps it running until the process exits.它跨测试共享服务器并保持它运行直到进程退出。 It also manages test tables for you, recreating them before each test.它还为您管理测试表,在每次测试之前重新创建它们。

class MyTest {
  @RegisterExtension
  TestDynamoDb db = new TestDynamoDb.Builder(JvmDynamoDbServer.Factory.INSTANCE) // or DockerDynamoDbServer
      // `MusicItem` is annotated with `@DynamoDBTable`. Tempest recreates this table before each test.
      .addTable(TestTable.create(MusicItem.TABLE_NAME, MusicItem.class))
      .build();

  @Test
  public void test() {
    PutItemRequest request = // ...;
    // Talk to the local DynamoDB.
    db.dynamoDb().putItem(request);
  }

}

There are couple of node.js wrappers for DynamoDB Local. DynamoDB Local 有几个 node.js 包装器。 These allows to easily execute unit tests combining with task runners like gulp or grunt.这些允许与任务运行器(如 gulp 或 grunt)结合轻松执行单元测试。 Try dynamodb-localhost , dynamodb-local尝试dynamodb-localhost , dynamodb-local

In Hadoop we also use DynamoDBLocal for testing and debugging work. 在Hadoop中,我们还使用DynamoDBLocal进行测试和调试工作。 Please see how it's used there as example at: https://github.com/apache/hadoop/blob/HADOOP-13345/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/s3guard/TestDynamoDBMetadataStore.java#L113 请参阅以下示例: https//github.com/apache/hadoop/blob/HADOOP-13345/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/ S3A / s3guard / TestDynamoDBMetadataStore.java#L113

I have found that the amazon repo as no index file, so does not seem to function in a way that allows you to bring it in like this:我发现 amazon repo 没有索引文件,因此似乎无法以允许您将其引入的方式运行:

maven {
   url = "https://s3-us-west-2.amazonaws.com/dynamodb-local/release"
}

The only way I could get the dependencies to load is by downloading DynamoDbLocal as a jar and bringing it into my build script like this:我可以加载依赖项的唯一方法是将 DynamoDbLocal 作为 jar 下载并将其放入我的构建脚本中,如下所示:

dependencies {
    ...
    runtime files('libs/DynamoDBLocal.jar')
    ...
}

Of course this means that all the SQLite and Jetty dependencies need to be brought in by hand - I'm still trying to get this right.当然,这意味着所有 SQLite 和 Jetty 依赖项都需要手动引入 - 我仍在努力解决这个问题。 If anyone knows of a reliable repo for DynamoDbLocal, I would really love to know.如果有人知道 DynamoDbLocal 的可靠存储库,我真的很想知道。

You could also use this lightweight test container 'Dynalite'你也可以使用这个轻量级的测试容器“Dynalite”

https://www.testcontainers.org/modules/databases/dynalite/ https://www.testcontainers.org/modules/databases/dynalite/

From testcontainers:从测试容器:

Dynalite is a clone of DynamoDB, enabling local testing. Dynalite 是 DynamoDB 的克隆,支持本地测试。 It's light and quick to run.运行起来轻便快捷。

Shortest solution with fix for sqlite4java.SQLiteException UnsatisfiedLinkError if it is a java/kotlin project built with gradle (a changed $PATH is not needed).与修为最短的解决方案sqlite4java.SQLiteException UnsatisfiedLinkError如果是带内置的gradle一个Java /科特林项目(一个改变$PATH必要)。

repositories {
    // ... other dependencies
    maven { url 'https://s3-us-west-2.amazonaws.com/dynamodb-local/release' } 
}

dependencies {
    testImplementation("com.amazonaws:DynamoDBLocal:1.13.6")
}

import org.gradle.internal.os.OperatingSystem
test {
    doFirst {
        // Fix for: UnsatisfiedLinkError -> provide a valid native lib path
        String nativePrefix = OperatingSystem.current().nativePrefix
        File nativeLib = sourceSets.test.runtimeClasspath.files.find {it.name.startsWith("libsqlite4java") && it.name.contains(nativePrefix) } as File
        systemProperty "sqlite4java.library.path", nativeLib.parent
    }
}

Straightforward usage in test classes ( src/test ):在测试类( src/test )中直接使用:

private lateinit var db: AmazonDynamoDBLocal

@BeforeAll
fun runDb() { db = DynamoDBEmbedded.create() }

@AfterAll
fun shutdownDb() { db.shutdown() }

The DynamoDB Gradle dependency already includes the SQLite libraries. DynamoDB Gradle 依赖项已包含SQLite 库。 You can pretty easily instruct the Java runtime to use it in your Gradle build script.您可以很容易地指示 Java 运行时在您的 Gradle 构建脚本中使用它。 Here's my build.gradle.kts as an example:以我的build.gradle.kts为例:

import org.apache.tools.ant.taskdefs.condition.Os

plugins {
    application
}

repositories {
    mavenCentral()
    maven {
        url = uri("https://s3-us-west-2.amazonaws.com/dynamodb-local/release")
    }
}

dependencies {
    implementation("com.amazonaws:DynamoDBLocal:[1.12,2.0)")
}

fun getSqlitePath(): String? {
    val dirName = when {
        Os.isFamily(Os.FAMILY_MAC) -> "libsqlite4java-osx"
        Os.isFamily(Os.FAMILY_UNIX) -> "libsqlite4java-linux-amd64"
        Os.isFamily(Os.FAMILY_WINDOWS) -> "sqlite4java-win32-x64"
        else -> throw kotlin.Exception("DynamoDB emulator cannot run on this platform")
    }
    return project.configurations.runtimeClasspath.get().find { it.name.contains(dirName) }?.parent
}

application {
    mainClass.set("com.amazonaws.services.dynamodbv2.local.main.ServerRunner")
    applicationDefaultJvmArgs = listOf("-Djava.library.path=${getSqlitePath()}")
}

tasks.named<JavaExec>("run") {
    args("-inMemory")
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM