web-dev-qa-db-ja.com

より簡単なDynamoDBローカルテスト

単体テストに DynamoDB local を使用しています。悪くはありませんが、いくつかの欠点があります。具体的には:

  • テストを実行する前に、どういうわけかサーバーを起動する必要があります
  • サーバーは各テストの前に起動および停止されないため、各テストの後にすべてのテーブルを削除するコードなどを追加しない限り、テストは相互に依存します。
  • すべての開発者がインストールする必要があります

私がやりたいのは、DynamoDBローカルjarと、それが依存する他のjarをtest/resourcesディレクトリに置くようなものです(Javaを書いています)。次に、各テストの前に、-inMemory、およびテスト後に停止します。そうすることで、gitリポジトリを取得する人は、テストを実行するために必要なすべてのコピーを取得し、各テストは他のテストから独立しています。

私はこの仕事をする方法を見つけましたが、それはいので、私は代替案を探しています。私が持っている解決策は、DynamoDBローカルのものの.Zipファイルをtest/resourcesに配置し、@ Beforeメソッドで一時ディレクトリに抽出して、新しいJava =それを実行するプロセス。それは機能しますが、見苦しく、いくつかの欠点があります。

  • 誰もが$ =でJava実行可能ファイルを必要とします
  • Zipをローカルディスクに解凍する必要があります。ローカルディスクを使用することは、特に継続的なビルドなどの場合、テストには困難です。
  • プロセスを生成して、各ユニットテストの開始を待機し、各テストの後にそのプロセスを強制終了する必要があります。遅いことに加えて、残されたプロセスの可能性はseemsいようです。

もっと簡単な方法があるはずです。結局のところ、DynamoDB LocalはJavaコードです。JVMにそれ自体をフォークし、リソースを調べてクラスパスを構築するように依頼することはできませんか?または、さらに良いことはできませんか?他のスレッドからDynamoDb Localのmainメソッドを呼び出すだけで、これはすべて単一のプロセスで発生しますか?

PS:私はオルタネーターを知っていますが、他の欠点があるように見えるので、それを機能させることができればAmazonのサポートされているソリューションに固執する傾向があります。

42
Oliver Dain

DynamoDBLocalを使用するには、次の手順に従う必要があります。

  1. 直接DynamoDBLocal依存関係を取得
  2. ネイティブSQLite4Java依存関係を取得する
  3. sqlite4Java.library.pathを設定してネイティブライブラリを表示します

1。直接DynamoDBLocal依存関係を取得

これは簡単です。 AWSフォーラム で説明されているように、このリポジトリが必要です。

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

この announcement に示すように、DynamoDB LocalをテストコードでMavenテストの依存関係として使用できます。 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();

これは、Gradleユーザー向けのbhdrknの回答を言い換えたものです(彼はMavenに基づいています)。まだ同じ3つのステップです。

  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を設定します

テストのためにcopyNativeDepsを実行し、sqlite4Javaにファイルの場所を指示するようGradleに指示する必要があります。 build.gradleファイルに追加...

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

2018年8月 Amazon発表 新規 Dockerイメージ Amazon DynamoDB Localを搭載。 JARをダウンロードして実行したり、サードパーティのOS固有のバイナリを使用して追加したりする必要はありません(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 Pipelines:

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の構成に焦点を当て、開発者が快適にコンテナを起動できるようにする必要があると思います。

サーバーは各テストの前に起動および停止されないため、各テストの後にすべてのテーブルを削除するコードなどを追加しない限り、テストは相互に依存します。

それは本当ですが、...そのような重いものを開始および停止し、各テストの前後にテーブルを再作成しないでください。 DBテストはほとんど常に相互依存関係にあり、それは問題ありません。各テストケースに一意の値を使用するだけです(たとえば、アイテムのハッシュキーをチケットID /作業中の特定のテストケースIDに設定します)。シードデータについては、ビルドツールやテストコードからも移動することをお勧めします。必要なすべてのデータを使用して独自のイメージを作成するか、AWS CLIを使用してテーブルを作成し、データを挿入します。単一の責任の原則と依存関係の注入の原則に従います。テストコードはテスト以外のことをしてはなりません。すべての環境(この場合、テーブルとデータを提供する必要があります)。実際にはテーブルが既に存在するため、テストでテーブルを作成するのは間違っています(もちろん、実際にテーブルを作成するメソッドをテストする場合を除きます)。

すべての開発者がインストールする必要があります

Dockerは2018年のすべての開発者にとって必須のものであるはずなので、それは問題ではありません。


また、JUnit 5を使用している場合は、テストにクライアントを注入する DynamoDB Local extension を使用することをお勧めします(はい、自己宣伝をしています)。

  1. JCenter リポジトリをビルドに追加します。

    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

    repositories {
        jcenter()
    }
    
  2. by.dev.madhead.aws-junit5:dynamodb-v1への依存関係を追加します

    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

    dependencies {
        testImplementation("by.dev.madhead.aws-junit5:dynamodb-v1:1.0.0")
    }
    
  3. テストで拡張機能を使用します。

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

上記の回答を2つの JUnitルール にラップしました。ルールはネイティブライブラリを処理するため、ビルドスクリプトを変更する必要はありません。私はアイデアが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());
  }
}

Amazonレポはインデックスファイルがないため、次のように取り込むことができるようには機能していないようです。

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

依存関係をロードできる唯一の方法は、DynamoDbLocalをjarとしてダウンロードし、次のようにビルドスクリプトに取り込むことです。

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

もちろん、これはすべてのSQLiteとJettyの依存関係を手で持ち込む必要があることを意味します-私はまだこれを正しくしようとしています。誰かがDynamoDbLocalの信頼できるレポを知っているなら、私は本当に知りたいです。

0
Michael Coxon

Hadoopでは、テストとデバッグ作業にもDynamoDBLocalを使用します。以下の例で使用方法をご覧ください: https://github.com/Apache/hadoop/blob/HADOOP-13345/hadoop-tools/hadoop-aws/src/test/Java/org/Apache/ hadoop/fs/s3a/s3guard/TestDynamoDBMetadataStore.Java#L11

0
Mingliang Liu

職場での単体テストでは、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;
        }
    });

それがあなたの探しているものかどうかはわかりませんが、それが私たちのやり方です。

0
Steve Smith

DynamoDB Local用のnode.jsラッパーがいくつかあります。これにより、gulpやgruntなどのタスクランナーと組み合わせて、単体テストを簡単に実行できます。 dynamodb-localhostdynamodb-local を試してください

0
Ashan