DynamoDB Localを使用したテスト
概要
Amazon DynamoDBを、AWSに依存せず、ローカルでテストする流れのメモ。
DynamoDBでは、AWSを利用せずとも、ローカルで検証できるように、AWS自身からDynamoDB Localというモジュールが提供されています。
このモジュールはAWSが管理しているMavenリポジトリにもホスティングされており、これを利用することで、事前に特別なコマンド等を発行することなく、JVM言語のビルドプロセス過程で、ローカルにDynamoDBを用意することができるようになります。
このポストでは、Kotlinプロジェクトで、DynamoDB Localを使用してテストする場合に、どのような設定が必要になるのかを中心に書いていきます。
この手順で使用したコードは、以下に公開しているので、こちらも参考にしてください。
https://github.com/yo1000/ddb-local/tree/40c9061dc6/ddb-local-test
要件
環境
今回の作業環境は以下のとおりです。
- Java 1.8.0_131
- DynamoDB Local 1.11.86
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.12.5
BuildVersion: 16F2073
$ java -version
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
依存関係解決
pom.xml
ここが肝なので、とりあえず全文掲載してしまいます。要点は後述。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.yo1000</groupId>
<artifactId>ddb-local-test</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>ddb-local-test</name>
<description>DynamoDB Local Testing Example</description>
<properties>
<kotlin.compiler.incremental>true</kotlin.compiler.incremental>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<kotlin.version>1.2.10</kotlin.version>
<dynamodblocal.version>[1.11,2.0)</dynamodblocal.version>
<sqlite4java.version>1.0.392</sqlite4java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jre8</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>DynamoDBLocal</artifactId>
<version>${dynamodblocal.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.almworks.sqlite4java</groupId>
<artifactId>sqlite4java</artifactId>
<version>${sqlite4java.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<jvmTarget>${java.version}</jvmTarget>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
<configuration>
<argLine>-Dsqlite4java.library.path=${basedir}/target/dependencies</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>process-test-resources</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/dependencies</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>false</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>dynamodb-local-oregon</id>
<name>DynamoDB Local Release Repository</name>
<url>https://s3-us-west-2.amazonaws.com/dynamodb-local/release</url>
</repository>
<repository>
<id>dynamodb-local-tokyo</id>
<name>DynamoDB Local Release Repository</name>
<url>https://s3-ap-northeast-1.amazonaws.com/dynamodb-local-tokyo/release</url>
</repository>
</repositories>
</project>
sqlite4java
なぜDynamoDBを使うのに、SQLiteが出てくるのか、という部分ですが、DynamoDB Localでは、
- DynamoDBとインターフェースが等しいこと
- データを永続化できること
の2点が満たせればよいだけなので、内部実装としては、SQLiteを使用した永続化が行われています。そのため、この依存が必要になってきます。
maven-surefire-plugin
で、引数に-Dsqlite4java.library.path=${basedir}/target/dependencies
を渡して、SQLiteのライブラリパスを指定している部分からも、これを読み取ることができます。
maven-dependency-plugin
outputDirectory
に、${project.build.directory}/dependencies
を設定して、通常、~/.m2/repository
配下にしか保存されない依存ライブラリを、自身の環境下にコピーします。
この指定により、maven-surefire-plugin
で、引数に設定した-Dsqlite4java.library.path=${basedir}/target/dependencies
を参照可能になります。
dynamodb-local-oregon, dynamodb-local-tokyo
DynamoDB Localは、Maven Centralにアップロードされておらず、Amazon S3上に、必要なファイル群がアップロードされているだけなので、追加リポジトリの指定が必要になります。
また、依存解決するロケーションにより、ネットワーク的に有利なリージョンを選択することが可能です。使用可能なリージョン別のリポジトリについては、以下を確認してください。
https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/DynamoDBLocal.html#DynamoDBLocal.Maven
テスト
テストコード
テストの前に、組み込みDynamoDBを作成して、テストの後に、これを破棄するようなサンプルを用意します。テストでは、テーブルの作成と、設定通りにテーブルが作成されているかを確認してみます。
書いた内容以上のものはないくらいシンプルなものなので、これ以上の説明はありませんが、この最小限のサンプルを理解しておくことで、Spring Boot等への転用も容易になります。
package com.yo1000.dynamo.local
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB
import com.amazonaws.services.dynamodbv2.local.embedded.DynamoDBEmbedded
import com.amazonaws.services.dynamodbv2.model.*
import org.junit.After
import org.junit.Assert
import org.junit.Before
import org.junit.Test
/**
*
* @author yo1000
*/
class DynamoDBEmbeddedTest {
lateinit var dynamo: AmazonDynamoDB
@Before
fun createDB() {
dynamo = DynamoDBEmbedded.create().amazonDynamoDB()
}
@After
fun shutdownDB() {
dynamo.shutdown()
}
@Test
fun test_that_table_is_created_equally_with_setting() {
val tableName = "Stationery"
val hashKeyName = "item_id"
val readCapacityUnits = 1000L
val writeCapacityUnits = 1000L
val result = dynamo.createTable(CreateTableRequest()
.withTableName(tableName)
.withKeySchema(listOf(
KeySchemaElement(hashKeyName, KeyType.HASH)
))
.withAttributeDefinitions(listOf(
AttributeDefinition(hashKeyName, ScalarAttributeType.S)
))
.withProvisionedThroughput(
ProvisionedThroughput(readCapacityUnits, writeCapacityUnits))
)
val tableDesc = result.tableDescription
Assert.assertEquals(tableName, tableDesc.tableName)
Assert.assertEquals("[{AttributeName: $hashKeyName,KeyType: ${KeyType.HASH}}]",
tableDesc.keySchema.toString())
Assert.assertEquals("[{AttributeName: $hashKeyName,AttributeType: ${ScalarAttributeType.S}}]",
tableDesc.attributeDefinitions.toString())
Assert.assertEquals(readCapacityUnits, tableDesc.provisionedThroughput.readCapacityUnits)
Assert.assertEquals(writeCapacityUnits, tableDesc.provisionedThroughput.writeCapacityUnits)
Assert.assertEquals("ACTIVE", tableDesc.tableStatus)
Assert.assertEquals("arn:aws:dynamodb:ddblocal:000000000000:table/$tableName", tableDesc.tableArn)
val tables = dynamo.listTables()
Assert.assertEquals(1, tables.tableNames.size)
}
}
テスト実行
テストを実行して、結果を確認します。
$ ./mvnw clean test
[INFO] --- maven-surefire-plugin:2.19.1:test (default-test) @ ddb-local-test ---
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.yo1000.dynamo.local.DynamoDBEmbeddedTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.738 sec - in com.yo1000.dynamo.local.DynamoDBEmbeddedTest
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 16.650 s
[INFO] Finished at: 2018-01-14T13:08:05+09:00
[INFO] Final Memory: 54M/710M
[INFO] ------------------------------------------------------------------------