在之前的文章中,我描述了新的Java 2 AWS SDK,它为调用不同AWS服务的Java客户端提供了非阻塞IO支持。 在本文中,我将介绍一种用于单元测试AWS DynamoDB调用的方法。
有几种方法可以启动DynamoDB的本地版本–
1. AWS提供了一个DynamoDB本地
2. Localstack提供了一种在本地启动大量AWS服务的方法
3. DynamoDB Local的Docker版本
4. Dynalite ,DynamoDB的基于节点的实现
现在可以对应用程序进行单元测试,我需要能够在测试运行之前使用以下选项之一启动DynamoDB的嵌入式版本,然后在测试完成后将其关闭。 我采用了三种方法:
1.使用JUnit 5扩展 ,该扩展在内部启动AWS DynamoDB Local,并在测试后将其调低。
2.使用测试容器启动Docker版本DynamoDB Local
JUnit5扩展
JUnit5扩展提供了方便的挂钩点来启动
嵌入式版本的DynamoDB用于测试。 它通过将DynamoDB Local版本作为Maven依赖项来工作:
dependencies {
...
testImplementation("com.amazonaws:DynamoDBLocal:1.11.119")
...
}
这种依赖关系的一个复杂之处在于,有DynamoDB Local与之交互的本机组件(dll,.so等),并且为了将它们放在正确的位置,我依赖于Gradle任务:
task copyNativeDeps(type: Copy) {
mkdir "build/native-libs"
from(configurations.testCompileClasspath) {
include '*.dll'
include '*.dylib'
include '*.so'
}
into 'build/native-libs'
}
test {
dependsOn copyNativeDeps
}
它将本机库放在build / native-libs文件夹中,扩展名在内部将此路径设置为系统属性:
System.setProperty("sqlite4java.library.path", libPath.toAbsolutePath().toString())
这是JUnit5扩展的代码库,所有这些都已经连接起来-https://github.com/bijukunjummen/boot-with dynamodb / blob / master / src / test / kotlin / sample / dyn / rules / LocalDynamoExtension.kt
使用此扩展名的测试如下所示:
class HotelRepoTest {
companion object {
@RegisterExtension
@JvmField
val localDynamoExtension = LocalDynamoExtension()
@BeforeAll
@JvmStatic
fun beforeAll() {
val dbMigrator = DbMigrator(localDynamoExtension.syncClient!!)
dbMigrator.migrate()
}
}
@Test
fun saveHotel() {
val hotelRepo = DynamoHotelRepo(localDynamoExtension.asyncClient!!)
val hotel = Hotel(id = "1", name = "test hotel", address = "test address", state = "OR", zip = "zip")
val resp = hotelRepo.saveHotel(hotel)
StepVerifier.create(resp)
.expectNext(hotel)
.expectComplete()
.verify()
}
}
该代码可以与功能齐全的DynamoDB进行交互。
DynamoDB本地Docker的
该JUnit5扩展方法非常有效,但它需要与本地二进制一个额外的依赖在被拉到一个更简洁的方法可以是使用好。 Testcontainers旋转起来DynamoDB本地下列方式的泊坞窗版本:
class HotelRepoLocalDynamoTestContainerTest {
@Test
fun saveHotel() {
val hotelRepo = DynamoHotelRepo(getAsyncClient(dynamoDB))
val hotel = Hotel(id = "1", name = "test hotel", address = "test address", state = "OR", zip = "zip")
val resp = hotelRepo.saveHotel(hotel)
StepVerifier.create(resp)
.expectNext(hotel)
.expectComplete()
.verify()
}
companion object {
val dynamoDB: KGenericContainer = KGenericContainer("amazon/dynamodb-local:1.11.119")
.withExposedPorts(8000)
@BeforeAll
@JvmStatic
fun beforeAll() {
dynamoDB.start()
}
@AfterAll
@JvmStatic
fun afterAll() {
dynamoDB.stop()
}
fun getAsyncClient(dynamoDB: KGenericContainer): DynamoDbAsyncClient {
val endpointUri = "http://" + dynamoDB.getContainerIpAddress() + ":" +
dynamoDB.getMappedPort(8000)
val builder: DynamoDbAsyncClientBuilder = DynamoDbAsyncClient.builder()
.endpointOverride(URI.create(endpointUri))
.region(Region.US_EAST_1)
.credentialsProvider(StaticCredentialsProvider
.create(AwsBasicCredentials
.create("acc", "sec")))
return builder.build()
}
...
}
}
此代码在一个随机的未占用端口上启动DynamoDB并提供此信息,以便可以使用此信息创建客户端。 根据此处报告的问题,我不得不采取一些Kotlin解决方法。
使用Dynalite的
Dynalite是DynamoDB的基于javascript的实现,可以使用TestContainer方法再次运行以进行测试。 但是,这次已经有了Dynalite的TestContainer模块 。 我发现它不支持JUnit5,并发送了Pull请求来提供此支持,在iterim中可以使用原始docker映像,这就是测试的样子:
class HotelRepoDynaliteTestContainerTest {
@Test
fun saveHotel() {
val hotelRepo = DynamoHotelRepo(getAsyncClient(dynamoDB))
val hotel = Hotel(id = "1", name = "test hotel", address = "test address", state = "OR", zip = "zip")
val resp = hotelRepo.saveHotel(hotel)
StepVerifier.create(resp)
.expectNext(hotel)
.expectComplete()
.verify()
}
companion object {
val dynamoDB: KGenericContainer = KGenericContainer("quay.io/testcontainers/dynalite:v1.2.1-1")
.withExposedPorts(4567)
@BeforeAll
@JvmStatic
fun beforeAll() {
dynamoDB.start()
val dbMigrator = DbMigrator(getSyncClient(dynamoDB))
dbMigrator.migrate()
}
@AfterAll
@JvmStatic
fun afterAll() {
dynamoDB.stop()
}
fun getAsyncClient(dynamoDB: KGenericContainer): DynamoDbAsyncClient {
val endpointUri = "http://" + dynamoDB.getContainerIpAddress() + ":" +
dynamoDB.getMappedPort(4567)
val builder: DynamoDbAsyncClientBuilder = DynamoDbAsyncClient.builder()
.endpointOverride(URI.create(endpointUri))
.region(Region.US_EAST_1)
.credentialsProvider(StaticCredentialsProvider
.create(AwsBasicCredentials
.create("acc", "sec")))
return builder.build()
}
...
}
}
结论
所有这些方法对于能够测试与DynamoDB的集成都是有用的。 我个人的喜好是使用TestContainers方法,如果通过JUnit5扩展方法可以使用其他Docker代理,则可以使用。 在我的github存储库中可以找到使用所有三种方法进行了完全正常工作的测试的示例– https://github.com/bijukunjummen/boot-with-dynamodb
翻译自: https://www.javacodegeeks.com/2019/01/testing-dynamodb-using-junit5.html