В предыдущем посте я описал новый AWS SDK для Java 2, который обеспечивает неблокирующую поддержку ввода-вывода для клиентов Java, вызывающих различные сервисы AWS. В этом посте я расскажу о подходе, которому я руководствовался при тестировании модулей вызовов AWS DynamoDB
Есть несколько способов раскрутить локальную версию DynamoDB —
1. AWS предоставляет локальный DynamoDB
2. Localstack предоставляет возможность раскрутить большое количество сервисов AWS локально
3. Докерская версия DynamoDB Local
4. Dynalite , основанная на узле реализация DynamoDB
Теперь, чтобы иметь возможность выполнить модульное тестирование приложения, мне нужно иметь возможность запустить встроенную версию DynamoDB с использованием одного из этих параметров непосредственно перед выполнением теста, а затем закрыть его после завершения теста. Есть три подхода, которые я выбрал:
1. Использование расширения JUnit 5, которое внутренне вызывает AWS DynamoDB Local и раскручивает его после теста.
2. Использование testcontainers для запуска док- версии DynamoDB Local
3. Использование тестконтейнеров для запуска DynaLite
Расширение JUnit5
Расширение JUnit5 обеспечивает удобную точку подключения для запуска
встроенная версия DynamoDB для тестов. Он работает путем добавления версии DynamoDB Local как зависимости maven:
1
2
3
4
5
|
dependencies { ... testImplementation( "com.amazonaws:DynamoDBLocal:1.11.119" ) ... } |
Сложность с этой зависимостью состоит в том, что есть нативные компоненты (dll, .so и т. Д.), С которыми взаимодействует DynamoDB Local, и чтобы получить их в нужном месте, я зависим от задачи Gradle:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
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, а расширение внутренне устанавливает этот путь как системное свойство:
1
|
System.setProperty( "sqlite4java.library.path" , libPath.toAbsolutePath().toString()) |
Вот кодовая база для расширения JUnit5 со всеми этими уже подключенными — https://github.com/bijukunjummen/boot-with dynamicodb / blob / master / src / test / kotlin / sample / dyn / rules / LocalDynamoExtension.kt
Тест с использованием этого расширения выглядит следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
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.
TestContainers с локальным докером DynamoDB
Подход расширений JUnit5 работает хорошо, но требует дополнительной зависимости с собственными двоичными файлами. Более чистый подход может заключаться в использовании превосходных Testcontainers для раскрутки док- версии DynamoDB Local следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
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 { 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. Однако на этот раз уже есть модуль TestContainer для Dynalite . Я обнаружил, что он не поддерживает JUnit5, и отправил запрос Pull для обеспечения этой поддержки, в нем можно использовать необработанный образ докера, и вот как выглядит тест:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
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 { 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. Образцы с полностью работающими тестами, использующими все три подхода, доступны в моем репозитории github — https://github.com/bijukunjummen/boot-with-dynamodb
Смотрите оригинальную статью здесь: модульное тестирование приложений DynamoDB с использованием JUnit5
Мнения, высказанные участниками Java Code Geeks, являются их собственными. |