【问题标题】:Easier DynamoDB local testing更轻松的 DynamoDB 本地测试
【发布时间】:2015-01-10 04:03:54
【问题描述】:

我正在使用DynamoDB local 进行单元测试。这还不错,但有一些缺点。具体来说:

  • 您必须在测试运行之前以某种方式启动服务器
  • 服务器不会在每次测试之前启动和停止,因此测试变得相互依赖,除非您在每次测试之后添加代码以删除所有表等
  • 所有开发者都需要安装它

我想要做的是将 DynamoDB 本地 jar 以及它所依赖的其他 jar 放在我的 test/resources 目录中(我正在用 Java 编写)。然后在每次测试之前我会启动它,使用-inMemory 运行,测试之后我会停止它。这样,任何拉下 git 存储库的人都会获得运行测试所需的所有内容的副本,并且每个测试都独立于其他测试。

我找到了一种方法来完成这项工作,但它很难看,所以我正在寻找替代方法。我的解决方案是将 DynamoDB 本地内容的 .zip 文件放在test/resources 中,然后在@Before 方法中,将其解压缩到某个临时目录并启动一个新的 java 进程来执行它。这行得通,但它很丑陋并且有一些缺点:

  • 每个人都需要$PATH 上的 java 可执行文件
  • 我必须将 zip 解压到本地磁盘。使用本地磁盘进行测试通常很冒险,尤其是在持续构建等情况下。
  • 我必须生成一个进程并等待它为每个单元测试启动,然后在每次测试后终止该进程。除了速度慢之外,剩余进程的可能性似乎也很难看。

似乎应该有一个更简单的方法。毕竟,DynamoDB Local 只是 Java 代码。我不能以某种方式要求 JVM 分叉自己并查看资源以构建类路径吗?或者,更好的是,我不能只从其他线程调用 DynamoDB Local 的main 方法,这样这一切都发生在一个进程中吗?有什么想法吗?

PS:我知道 Alternator,但它似乎还有其他缺点,所以我倾向于坚持使用亚马逊支持的解决方案,如果我能让它工作的话。

【问题讨论】:

  • 正如您所说,您想编写单元测试 - 而不是集成测试 - 为什么不使用模拟?像 DynamoDB-mock 之类的东西。这个允许to be encapsulated as library
  • @cheffe,谢谢你的想法。这似乎正是我想要的,但它是 Python,而不是 Java,所以我仍然必须从我的测试中生成一个外部可执行文件,就像我对 DynamoDB Local 所做的那样(并确保所有用户都有正确版本的 Python安装,在他们的 $PATH 上有那个,等等)。我正在寻找非常类似的东西,但是在 Java 中。请注意,创建自己的模拟将是一项艰巨的任务,因为 Dynamo API 非常丰富。

标签: java unit-testing junit amazon-dynamodb


【解决方案1】:

要使用 DynamoDBLocal,您需要执行以下步骤。

  1. 获取直接 DynamoDBLocal 依赖项
  2. 获取本机 SQLite4Java 依赖项
  3. 设置sqlite4java.library.path 显示原生库

1.获取直接 DynamoDBLocal 依赖项

这个很简单。您需要此存储库,如 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。获取原生 SQLite4Java 依赖项

如果您不添加这些依赖项,您的测试将失败并出现 500 内部错误。

首先,添加这些依赖:

<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>

然后,添加此插件以获取特定文件夹的本地依赖项:

<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.设置sqlite4java.library.path 显示原生库

作为最后一步,您需要将sqlite4java.library.path 系统属性设置为native-libs 目录。在创建本地服务器之前这样做是可以的。

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

完成这些步骤后,您可以根据需要使用 DynamoDBLocal。这是一个为此创建本地服务器的 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);
        }
    }
}

你可以这样使用这条规则

@RunWith(JUnit4.class)
public class UserDAOImplTest {

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

【讨论】:

  • 发现DynamoDBLocal依赖自动引入了sqlite4java,额外的依赖不需要手动指定。
  • @JefferyGrajkowski 我也尝试过,但如果没有本机库,我无法让它工作。你的 DDB 本地版本是多少?也许他们更新了依赖项。
  • 我使用com.amazonaws:DynamoDBLocal:1.+。我认为最好保持最新,因为无论我喜欢与否,服务本身也会更新。现在适用于 1.11.0。
  • 我尝试了这个解决方案以及@JefferyGrajkowski 的建议,它就像一个魅力。谢谢。
  • 很好的答案。我会将本机库放在目标中:&lt;outputDirectory&gt;${project.basedir}/native-libs&lt;/outputDirectory&gt;System.setProperty("sqlite4java.library.path", "target/native-libs");
【解决方案2】:

2018 年 8 月 Amazon announcedDocker image 板载 Amazon DynamoDB Local。它不需要下载和运行任何 JAR,也不需要使用第三方操作系统特定的二进制文件添加(我说的是sqlite4java)。

就像在测试前启动一个 Docker 容器一样简单:

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

您可以手动执行此操作以进行本地开发,如上所述,或在您的 CI 管道中使用它。许多 CI 服务提供了在管道期间启动其他容器的能力,这些容器可以为您的测试提供依赖项。以下是 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

或 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

等等。我们的想法是将您可以在otheranswers 中看到的所有配置移出您的构建工具,并在外部提供依赖项。将其视为依赖注入/IoC,但对于整个服务,而不仅仅是单个 bean。

启动容器后,您可以创建一个指向它的客户端:

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

现在回到原来的问题:

您必须在测试运行之前以某种方式启动服务器

您可以手动启动它,或者为它准备一个开发者脚本。 IDE 通常会提供一种在执行任务之前运行任意命令的方法,因此您可以make IDE 为您启动容器。我认为在这种情况下,在本地运行某些东西不应该是首要任务,而是应该专注于配置 CI,让开发人员启动容器,因为它对他们来说很舒服。

服务器不会在每次测试之前启动和停止,因此测试变得相互依赖,除非您在每次测试之后添加代码以删除所有表等

没错,但是……你不应该开始和停止这样重量级的事情 并在每次测试之前/之后重新创建表。数据库测试几乎总是相互依赖的,这对他们来说没问题。只需为每个测试用例使用唯一值(例如,将项目的哈希键设置为工单 ID/您正在处理的特定测试用例 ID)。至于种子数据,我建议将其从构建工具和测试代码中移出。使用您需要的所有数据制作您自己的映像,或者使用 AWS CLI 创建表和插入数据。遵循单一职责原则和依赖注入原则:你的测试代码除了测试不能做任何事情。所有环境(在这种情况下应为他们提供表格和数据)。在测试中创建表是错误的,因为在现实生活中该表已经存在(当然,除非您正在测试实际创建表的方法)。

所有开发者都需要安装它

Docker 应该是 2018 年每个开发者的必备工具,所以这不是问题。


如果您使用的是 JUnit 5,最好使用 DynamoDB Local extension 将客户端注入您的测试中(是的,我正在自我推销):

  1. 添加对me.madhead.aws-junit5:dynamodb-v1的依赖

    pom.xml

    <dependency>
        <groupId>me.madhead.aws-junit5</groupId>
        <artifactId>dynamo-v1</artifactId>
        <version>6.0.1</version>
        <scope>test</scope>
    </dependency>
    

    build.gradle

    dependencies {
        testImplementation("me.madhead.aws-junit5:dynamo-v1:6.0.1")
    }
    
  2. 在测试中使用扩展:

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

【讨论】:

  • 这对本地测试有什么作用?我不应该强迫每个可怜的开发人员安装 docker 并让 DynamoDBLocal 映像运行并精心配置。
  • 确实,2019 年怕 Docker 的开发者是个差劲的开发者。您仍然可以将这种方法用于 CI/CD,无论如何,一切都发生在 Docker 中(大多数现代 CI 服务器都是基于 Docker 的,甚至 Jenkins 也使用 Docker)。我的观点是,您不会用启动代码污染您的测试代码库,而只是在外部提供服务(DynamoDB)。它可以是 Docker 容器。或者它可以是DynamoDBLocal.jar。或者您可以运行localstack。在所有情况下,您的所有测试都需要知道 URL。
  • 这是一个很好的答案,非常感谢。我尝试在-scriptgitlab-ci.yml 中使用docker run 运行docker,并且在连接到这个docker 时遇到了巨大的问题(请参阅stackoverflow.com/questions/60326823/…)。您使用-services 提供的不错的解决方案就像一个魅力。
  • 能否请您描述一下如何在 Gitlab CI 上传递命令行参数-jar DynamoDBLocal.jar -sharedDb 以使数据库共享?
  • @madhead 相反,当您的 CI/CD 构建依赖于运行子容器时,这是一个巨大的痛苦。运行 docker-in-docker 会遇到很多问题。
【解决方案3】:

这是对 bhdrkn 对 Gradle 用户的回答的重述(他的回答基于 Maven)。还是一样的三步:

  1. 获取直接 DynamoDBLocal 依赖项
  2. 获取本机 SQLite4Java 依赖项
  3. 设置 sqlite4java.library.path 以显示本机库

1。获取直接 DynamoDBLocal 依赖项

添加到 build.gradle 文件的依赖项部分...

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

2。获取本机 SQLite4Java 依赖项

sqlite4java 库已经作为 DynamoDBLocal 的依赖项下载,但需要将库文件复制到正确的位置。添加到您的 build.gradle 文件中...

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

3。设置 sqlite4java.library.path 以显示本机库

我们需要告诉 Gradle 运行 copyNativeDeps 进行测试,并告诉 sqlite4java 在哪里可以找到文件。添加到您的 build.gradle 文件中...

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

【讨论】:

  • @Jeffery 我收到以下错误:testMapRtbUser STANDARD_ERROR 17:39:41.931 [DEBUG] [TestEventLogger] 2017-08-29 17:39:41.929:WARN:oejs.AbstractHttpConnection:/ 17: 39:41.931 [DEBUG] [TestEventLogger] java.lang.NoSuchMethodError: com.amazon.dynamodb.grammar.DynamoDbExpressionParser.parseAttributeValuesMapKeys(Ljava/lang/String;Lorg/antlr/v4/runtime/ANTLRErrorListener;)V 但是,对于相同测试我是否从 Eclipse 运行它作为 Junit 测试它运行良好。它只有在 gradle 作为测试运行时才会失败。后来这个超时是更新操作的超时错误。救命!
  • 听起来像是运行时类路径问题。该类和具有该签名的方法肯定存在于最新版本的 JAR 中。尝试清除 Gradle 缓存的所有内容,然后重试。
  • 我跟着这个,但我不断收到 java.lang.RuntimeException: com.amazonaws.SdkClientException: Unable to execute HTTP request: The target server failed to respond when I run my tests.知道我做错了什么吗?
  • 在手动启动 DynamoDBLocal 时,您是否设法运行和编写用户测试?尝试在自动化之前先编写简单的手册。
  • 要在 IntelliJ 中运行测试,请将 -Djava.library.path=build/libs 添加到运行/调试配置中的“VM 选项”中。
【解决方案4】:

您可以在测试代码中使用 DynamoDB Local 作为 Maven 测试依赖项,如 announcement 中所示。您可以通过 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();

您也可以在嵌入式模式下运行:

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

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

【讨论】:

  • 对于使用 ServerRunner 的第一个选项,它启动正常,但是一旦我尝试创建表,我就会得到 AmazonServiceException The request processing has failed because of an unknown error, exception or failure. (Service: AmazonDynamoDBv2; Status Code: 500; Error Code: InternalFailure; Request ID: ea0eff34-65e4-49d5-8ae9-3bfbfec9136e)
  • 对于嵌入式版本,我在 initializeMetadataTables 方法中从 SQLLite 获得了 NullPointerException。 :(
  • 确保提供 sqlite4java JNI 库的完整路径作为 -Dsqlite4java.library.path=/the/path/to/sqlite/for/java/jni/libraries 系统属性的一部分。
  • 在使用 inMemory 选项时是否需要这样做?
  • 官方repository containing example code好像没有这方面的信息。
【解决方案5】:

我已将上面的答案打包成两个JUnit rules,不需要更改构建脚本,因为规则会处理本机库的内容。我这样做是因为我发现 Idea 不喜欢 Gradle/Maven 解决方案,因为它刚开始就开始做自己的事情。

这意味着步骤是:

  • 获取AssortmentOfJUnitRules 1.5.32或以上版本的依赖
  • 获取直接 DynamoDBLocal 依赖项
  • 将 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.+"
}

代码:

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

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

【讨论】:

  • 我遇到了这个问题。我在LocalDynamoDbRule.java:38 中得到一个NPEDynamoDBEmbedded.create() 返回null)。在此之前,本机SQLite 部分记录:com.almworks.sqlite4java.SQLiteException: [-91] cannot load library: com.almworks.sqlite4java.SQLiteException: [-91] sqlite4java cannot find native library。我使用com.github.mlk:DynamoDBLocal:1.11.119com.github.mlk:assortmentofjunitrules:1.5.39。不应该LocalDynamoDbRule关心所有原生 SQLite 的东西?
  • Here 是我得到的错误的完整堆栈跟踪。
  • 它应该为你处理原生库的东西是的。我会调查的。谢谢
  • 你能把你的 POM/gradle/其他东西的截图发给我吗?谢谢。
  • 我已经能够用最少的 POM 进行复制。看来这是不影响 Gradle 的 Maven 问题。我会继续调查。 github.com/mlk/AssortmentOfJUnitRules/issues/2
【解决方案6】:

试试tempest-testing!它提供了一个 JUnit4 规则和一个 JUnit5 扩展。它还支持 AWS SDK v1 和 SDK v2。

Tempest 提供了一个用于测试 DynamoDB 客户端的库 使用DynamoDBLocal .它有两种实现方式:

  • JVM:这是首选选项,运行由sqlite4java 支持的DynamoDBProxyServer, 可在most platforms 获得。
  • Docker: 这在 Docker 中运行 dynamodb-local 容器。

特征矩阵:

Feature tempest-testing-jvm tempest-testing-docker
Start up time ~1s ~10s
Memory usage Less More
Dependency sqlite4java native library Docker

要使用tempest-testing,首先将此库添加为测试依赖项:

对于 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"
}

对于 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"
}

然后在带有@org.junit.jupiter.api.Test 注释的测试中,您可以添加TestDynamoDb 作为测试 extension。这个扩展启动了一个 DynamoDB 服务器。它在测试之间共享服务器并保持运行直到进程退出。它 还为您管理测试表,在每次测试之前重新创建它们。

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

}

【讨论】:

  • 我将一些现有的基于容器的测试代码迁移到了这个库的 JVM 模式,运行良好,速度也不错。
【解决方案7】:

对于工作中的单元测试,我使用 Mockito,然后只模拟 AmazonDynamoDBClient。然后使用 when 模拟返回。像下面这样:

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

不确定这是否是您想要的,但我们就是这样做的。

【讨论】:

  • 感谢您的想法。模拟是可以的,但很难让协议完全正确。因此,如果假设 Dynamo(或其他任何东西)的行为方式与您认为的方式(您模拟它的方式)一致,那么您最终会测试您的代码是否有效,但您并没有测试您的代码是否真的可以与 Dynamo 一起使用。如果您对 Dynamo 的工作方式有误,您的代码和测试会做出相同的假设,所以事情通过了,但您有错误。
  • 听起来你在做集成测试,因为你不应该做很多测试。你只是想确保你能做基本的操作。基本上验证您是否已正确连接。过去我要做的是在测试中启动一个本地实例。那么您将拥有从本地数据库中保存、读取和删除的硬编码值。除此之外,你在做什么这些测试?我总是建议进行单元测试(测试只是我模拟的一件事)和集成测试(一切真实)
  • 理论上 mocking 是单元测试的正确方式,但是本地 DDB 可以以更有前途的方式确保代码正确。
【解决方案8】:

最短解决方案,如果它是使用 gradle 构建的 java/kotlin 项目,则修复 sqlite4java.SQLiteException UnsatisfiedLinkError不需要需要更改 $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
    }
}

在测试类中直接使用 (src/test):

private lateinit var db: AmazonDynamoDBLocal

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

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

【讨论】:

    【解决方案9】:

    似乎应该有一个更简单的方法。毕竟,DynamoDB Local 只是 Java 代码。我不能以某种方式要求 JVM 分叉自己并查看资源以构建类路径吗?

    您可以按照这些思路做一些事情,但要简单得多:以编程方式在类路径中搜索本机库的位置,然后在启动 DynamoDB 之前设置 sqlite4java.library.path 属性。这是在 tempest-testingthis answer (code here) 中实现的方法,这就是为什么它们只是作为纯库/类路径依赖项工作的原因,仅此而已。

    在我的情况下,需要在 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();
      }
    

    没有机会在很多平台上进行测试,错误处理可能会有所改进。

    很遗憾 AWS 没有花时间使库更友好,因为这可以在库代码本身中轻松完成。

    【讨论】:

      【解决方案10】:

      DynamoDB Local 有几个 node.js 包装器。这些允许结合任务运行器(如 gulp 或 grunt)轻松执行单元测试。试试dynamodb-localhost, dynamodb-local

      【讨论】:

      • 你测试过这个 npm 模块吗?
      • 我是这些的创造者。我们经常使用它们。
      • 哈哈..是的我安装了..让我们试试
      • dynamodb-localhost 模块有一些 UI 错误,难以使用
      • 用户界面错误?你能进一步详细说明吗?这是 serverless-dynamodb-local 插件作为其核心使用的库,每周有数千次下载。因此,如果您能提及您遇到的任何问题,将会很有帮助。
      【解决方案11】:

      我发现亚马逊 repo 没有索引文件,因此似乎无法以允许您像这样引入它的方式运行:

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

      我可以加载依赖项的唯一方法是将 DynamoDbLocal 下载为 jar 并将其引入我的构建脚本,如下所示:

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

      当然,这意味着所有 SQLite 和 Jetty 依赖项都需要手动引入 - 我仍在努力解决这个问题。如果有人知道 DynamoDbLocal 的可靠存储库,我真的很想知道。

      【讨论】:

        【解决方案12】:

        您也可以使用这个轻量级测试容器“Dynalite”

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

        来自测试容器:

        Dynalite 是 DynamoDB 的克隆,支持本地测试。很轻 并且运行迅速。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-12-05
          • 2013-05-25
          • 1970-01-01
          • 2011-04-16
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多