単体テストに DynamoDB local を使用しています。悪くはありませんが、いくつかの欠点があります。具体的には:
私がやりたいのは、DynamoDBローカルjarと、それが依存する他のjarをtest/resourcesディレクトリに置くようなものです(Javaを書いています)。次に、各テストの前に、-inMemory
、およびテスト後に停止します。そうすることで、gitリポジトリを取得する人は、テストを実行するために必要なすべてのコピーを取得し、各テストは他のテストから独立しています。
私はこの仕事をする方法を見つけましたが、それはいので、私は代替案を探しています。私が持っている解決策は、DynamoDBローカルのものの.Zipファイルをtest/resourcesに配置し、@ Beforeメソッドで一時ディレクトリに抽出して、新しいJava =それを実行するプロセス。それは機能しますが、見苦しく、いくつかの欠点があります。
もっと簡単な方法があるはずです。結局のところ、DynamoDB LocalはJavaコードです。JVMにそれ自体をフォークし、リソースを調べてクラスパスを構築するように依頼することはできませんか?または、さらに良いことはできませんか?他のスレッドからDynamoDb Localのmain
メソッドを呼び出すだけで、これはすべて単一のプロセスで発生しますか?
PS:私はオルタネーターを知っていますが、他の欠点があるように見えるので、それを機能させることができればAmazonのサポートされているソリューションに固執する傾向があります。
DynamoDBLocalを使用するには、次の手順に従う必要があります。
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();
}
この 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つのステップです。
- 直接DynamoDBLocal依存関係を取得
- ネイティブSQLite4Java依存関係を取得する
- ネイティブライブラリを表示するようにsqlite4Java.library.pathを設定します
Build.gradleファイルの依存関係セクションに追加...
dependencies {
testCompile "com.amazonaws:DynamoDBLocal:1.+"
}
Sqlite4JavaライブラリはDynamoDBLocalの依存関係として既にダウンロードされますが、ライブラリファイルは適切な場所にコピーする必要があります。 build.gradleファイルに追加...
task copyNativeDeps(type: Copy) {
from(configurations.compile + configurations.testCompile) {
include '*.dll'
include '*.dylib'
include '*.so'
}
into 'build/libs'
}
テストのためにcopyNativeDeps
を実行し、sqlite4Javaにファイルの場所を指示するようGradleに指示する必要があります。 build.gradleファイルに追加...
test {
dependsOn copyNativeDeps
systemProperty "Java.library.path", 'build/libs'
}
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 を使用することをお勧めします(はい、自己宣伝をしています)。
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()
}
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")
}
テストで拡張機能を使用します。
@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();
}
}
上記の回答を2つの JUnitルール にラップしました。ルールはネイティブライブラリを処理するため、ビルドスクリプトを変更する必要はありません。私はアイデアがGradle/Mavenソリューションを好まなかったことがわかったので、これをやったので、それが消えて独自のことをやりました。
つまり、手順は次のとおりです。
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の信頼できるレポを知っているなら、私は本当に知りたいです。
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
職場での単体テストでは、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;
}
});
それがあなたの探しているものかどうかはわかりませんが、それが私たちのやり方です。
DynamoDB Local用のnode.jsラッパーがいくつかあります。これにより、gulpやgruntなどのタスクランナーと組み合わせて、単体テストを簡単に実行できます。 dynamodb-localhost 、 dynamodb-local を試してください