当前位置: 首页 > 知识库问答 >
问题:

更简单的DynamoDB本地测试

黄信厚
2023-03-14

我正在使用DynamoDB local进行单元测试。这并不坏,但也有一些缺点。明确地:

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

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

我已经找到了一种方法来实现这一目标,但它很丑陋,所以我正在寻找替代方案。我的解决方案是将DynamoDB本地内容的. zip文件放在test/Resources中,然后在@之前方法中,将其提取到某个临时目录并启动一个新的java进程来执行它。这很有效,但它很丑陋,也有一些缺点:

  • 每个人都需要在他们的$PATH

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

PS:我知道交流发电机,但它似乎有其他缺点,所以我倾向于坚持亚马逊支持的解决方案,如果我能让它工作。

共有3个答案

马琛
2023-03-14

这是bhdrkn为Gradle用户提供的答案的重述(他的答案基于Maven)。这仍然是相同的三个步骤:

  1. 获得直接的动态相关性

添加到构建的依赖项部分。格雷德尔档案。。。

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

sqlite4java库已经作为DynamoDBLocal的依赖项下载,但是库文件需要复制到正确的位置。添加到你的构建中。格雷德尔档案。。。

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

我们需要告诉Gradle运行copyNativeDeps进行测试,并告诉sqlite4java在哪里找到文件。添加到你的构建中。格雷德尔档案。。。

test {
    dependsOn copyNativeDeps
    systemProperty "java.library.path", 'build/libs'
}
欧阳安晏
2023-03-14

2018年8月,亚马逊宣布了新的码头工人形象,亚马逊DynamoDB本地搭载。它不需要下载和运行任何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

等等。这个想法是将您可以在其他答案中看到的所有配置移出构建工具,并在外部提供依赖关系。可以把它想象成依赖注入/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通常提供在执行任务之前运行任意命令的方法,因此您可以使用IDE来启动容器。我认为,在这种情况下,在本地运行某些东西不应该是首要任务,相反,您应该专注于配置CI,并让开发人员在他们觉得舒服的时候启动容器。

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

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

所有开发人员都需要安装它

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

如果您正在使用JUnit 5,那么使用DynamoDB本地扩展将客户端注入您的测试是一个好主意(是的,我正在做自运营):

>

  • 添加对me.madhead.aws的依赖

    波姆。xml:

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

    建筑格拉德尔

    dependencies {
        testImplementation("me.madhead.aws-junit5:dynamo-v1:6.0.1")
    }
    

    在测试中使用扩展:

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

  • 端木高邈
    2023-03-14

    为了使用DynamoDBLocal,您需要遵循以下步骤。

    1. 获得直接的动态相关性
    2. 获取本机SQLite4Java依赖项
    3. 设置sqlite4java。图书馆显示本机库的路径

    1.获得直接的动态依赖关系

    这个很简单。正如这里所解释的,您需要这个存储库。

    <!--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。图书馆本机libs目录的路径system属性。在创建本地服务器之前这样做是可以的。

    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();
    }
    
     类似资料:
    • 问题内容: AFAIK可以通过进行最灵活的gson定制,但是它可能变得不必要地复杂。它迫使我为每个处理的类和都编写代码,而有时只需要一种方法。此外,有时和/或更容易编写,例如在这里。这使我想到了以下问题: 是否可以编写一个仅委托其方法之一(例如,从写入到写入)的a? 是否可以以某种方式使用和/或与它一起使用?另外,他们有工厂吗? 问题答案: 可以创建一个委派其方法之一的方法。这个用例是API的重要

    • 尽管Amazon提供了关于如何使用Java、PHP和.Net连接到dynamoDB local的文档,但没有描述如何使用Python连接到localhost:8000。web上的现有文档指向在boto.dynamodb2.layer1中使用dynamoDB连接方法,但这会在使用boto3协议管理dynamoDB的实时和测试环境之间造成不兼容。 在boto3中,您可以使用以下构造函数和设置到环境中的

    • 我一直试图获得本地单元测试的代码覆盖率,但没有成功。 这里有一个关于我所说的本地单元测试的参考。 https://developer.android.com/training/testing/unit-testing/local-unit-tests.html 但这似乎无济于事。我是不是漏掉了什么? 注意,如果我通过Android Studio运行本地单元测试,一切都很好。我点击了我的“测试”模块

    • 我创建了“快速启动:Web后端”AWS SAM项目,其中包含一个使用的DynamoDB表。 按照自述文件中的说明,当我尝试构建并调用引用Dynamo表的lambda函数时: 我得到一个错误: 示例项目包含3个lambda函数,它们调用DynamoDb后端(为清晰起见进行了修剪): 我的假设是DynamoDB表不是在AWS测试docker容器中本地创建的。在本地测试DynamoDB时,是否还缺少其他

    • import { CounterActions, INCREMENT_COUNTER, DECREMENT_COUNTER, } from './counter'; // Mock out the NgRedux class with just enough to test what we want. class MockRedux extends NgRedux<any> { c

    • 问题内容: 为了“有趣”,我将一些Java类轻松地移植到PHP(例如),因此我希望能够运行/移植这些单元测试: 在哪里可以找到它们?任何最新版本的测试都将有所帮助。 问题答案: OpenJDK项目在线提供其源代码。例如,可以在http://hg.openjdk.java.net/jdk7u/jdk7u2/jdk/file/58ad18490a50/test/java/lang/StringBuil