J'utilise DynamoDB local pour les tests unitaires. Ce n'est pas mauvais, mais a quelques inconvénients. Plus précisément:
Ce que je veux faire, c'est comme mettre le fichier jar local DynamoDB et les autres fichiers jar dont il dépend dans mon répertoire test/resources (j'écris en Java). Ensuite, avant chaque test, je le lancerais avec -inMemory
et, après le test, je l'arrêterais. Ainsi, toute personne qui rédige le rapport git reçoit une copie de tout ce dont elle a besoin pour exécuter les tests et chaque test est indépendant des autres.
J'ai trouvé un moyen de faire ce travail, mais c'est moche, alors je cherche des alternatives. La solution que j'ai est de mettre un fichier .Zip du matériel local DynamoDB dans test/resources, puis dans une méthode @Before, je l'extrais dans un répertoire temporaire et je lance un nouveau processus Java pour l'exécuter. Cela fonctionne, mais c'est moche et présente quelques inconvénients:
Il semble qu'il devrait y avoir un moyen plus facile. DynamoDB Local est, après tout, juste du code Java. Est-ce que je ne peux pas en quelque sorte demander à la JVM de se bichonner et de regarder à l'intérieur des ressources pour créer un chemin de classe? Ou, mieux encore, ne puis-je pas simplement appeler la méthode main
de DynamoDb Local à partir d'un autre thread afin que tout cela se produise en un seul processus? Des idées?
PS: Je connais Alternator, mais il semble présenter d'autres inconvénients, et je suis donc enclin à rester avec la solution prise en charge par Amazon si je parviens à le faire fonctionner.
Pour utiliser DynamoDBLocal, vous devez suivre ces étapes.
sqlite4Java.library.path
pour afficher les bibliothèques natives1. Obtenir une dépendance directe DynamoDBLocal
Celui-ci est le facile. Vous avez besoin de ce référentiel comme expliqué dans AWS Forums .
<!--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. Obtenir les dépendances SQLite4Java natives
Si vous n'ajoutez pas ces dépendances, vos tests échoueront avec une erreur interne de 500.
Tout d'abord, ajoutez ces dépendances:
<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>
Ajoutez ensuite ce plugin pour obtenir des dépendances natives dans un dossier spécifique:
<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. Définir sqlite4Java.library.path
pour afficher les bibliothèques natives
Enfin, vous devez définir la propriété système sqlite4Java.library.path
dans le répertoire native-libs. Vous pouvez le faire juste avant de créer votre serveur local.
System.setProperty("sqlite4Java.library.path", "native-libs");
Après ces étapes, vous pouvez utiliser DynamoDBLocal à votre guise. Voici une règle Junit qui crée un serveur local pour cela.
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);
}
}
}
Vous pouvez utiliser cette règle comme ceci
@RunWith(JUnit4.class)
public class UserDAOImplTest {
@ClassRule
public static final LocalDynamoDBCreationRule dynamoDB = new LocalDynamoDBCreationRule();
}
Vous pouvez utiliser DynamoDB Local comme dépendance de test Maven dans votre code de test, comme indiqué dans cette annonce . Vous pouvez exécuter sur 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();
Vous pouvez également exécuter en mode intégré:
import com.amazonaws.services.dynamodbv2.local.embedded.DynamoDBEmbedded;
AmazonDynamoDB dynamodb = DynamoDBEmbedded.create();
dynamodb.listTables();
C’est une reformulation de la réponse de bhdrkn pour les utilisateurs de Gradle (celle-ci est basée sur Maven). Ce sont toujours les mêmes trois étapes:
- Obtenir une dépendance directe DynamoDBLocal
- Obtenir les dépendances natives SQLite4Java
- Définir sqlite4Java.library.path pour afficher les bibliothèques natives
Ajoutez à la section des dépendances de votre fichier build.gradle ...
dependencies {
testCompile "com.amazonaws:DynamoDBLocal:1.+"
}
Les bibliothèques sqlite4Java seront déjà téléchargées en tant que dépendance de DynamoDBLocal, mais les fichiers de la bibliothèque doivent être copiés au bon endroit. Ajoutez à votre fichier build.gradle ...
task copyNativeDeps(type: Copy) {
from(configurations.compile + configurations.testCompile) {
include '*.dll'
include '*.dylib'
include '*.so'
}
into 'build/libs'
}
Nous devons dire à Gradle d’exécuter copyNativeDeps
pour les tests et à sqlite4Java où trouver les fichiers. Ajoutez à votre fichier build.gradle ...
test {
dependsOn copyNativeDeps
systemProperty "Java.library.path", 'build/libs'
}
J'ai résumé les réponses ci-dessus dans deux JUnit rules qui ne nécessite aucune modification du script de construction car les règles gèrent les éléments de la bibliothèque native. C'est ce que j'ai fait, car j'ai constaté qu'Idea n'aimait pas les solutions Gradle/Maven.
Cela signifie que les étapes sont les suivantes:
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.+"
}
Code:
public class LocalDynamoDbRuleTest {
@Rule
public LocalDynamoDbRule ddb = new LocalDynamoDbRule();
@Test
public void test() {
doDynamoStuff(ddb.getClient());
}
}
En août 2018 Amazon annoncé new image Docker avec Amazon DynamoDB Local intégré. Il n'est pas nécessaire de télécharger et d'exécuter des fichiers JAR, ni d'ajouter des fichiers binaires tiers spécifiques au système d'exploitation (je parle de sqlite4Java
).
C’est aussi simple que de démarrer un conteneur Docker avant les tests:
docker run -p 8000:8000 Amazon/dynamodb-local
Vous pouvez le faire manuellement pour le développement local, comme décrit ci-dessus, ou l'utiliser dans votre pipeline CI. De nombreux services de CI offrent la possibilité de démarrer des conteneurs supplémentaires pendant le pipeline, ce qui peut fournir des dépendances pour vos tests. Voici un exemple pour 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
Ou Pipelines Bitbucket:
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
Etc. L'idée est de déplacer toute la configuration que vous pouvez voir dans autre _ { réponses de votre outil de génération et de fournir la dépendance en externe. Pensez-y à l'injection de dépendance/IoC mais à l'ensemble du service, pas à un seul haricot.
Après avoir démarré le conteneur, vous pouvez créer un client pointant vers celui-ci:
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();
}
Passons maintenant aux questions initiales:
Vous devez en quelque sorte démarrer le serveur avant l'exécution de vos tests
Vous pouvez simplement le démarrer manuellement ou préparer un script pour les développeurs. Les IDE fournissent généralement un moyen d'exécuter des commandes arbitraires avant d'exécuter une tâche. Vous pouvez donc make IDE } _ démarrer le conteneur pour vous. Je pense qu'exécuter quelque chose localement ne devrait pas être une priorité absolue dans ce cas, mais vous devriez plutôt vous concentrer sur la configuration de CI et laisser les développeurs démarrer le conteneur comme il leur convient.
Le serveur n'étant ni démarré ni arrêté avant chaque test, les tests deviennent interdépendants, sauf si vous ajoutez du code pour supprimer toutes les tables, etc. après chaque test.
C'est vrai, mais… Vous ne devriez pas commencer et arrêter des choses aussi lourdes Et recréer des tables avant/après chaque test. Les tests de base de données sont presque toujours interdépendants et cela leur convient. Utilisez simplement des valeurs uniques pour chaque cas de test (par exemple, la clé de hachage de l'élément à l'ID de ticket/l'ID de cas de test spécifique sur lequel vous travaillez). En ce qui concerne les données de départ, je vous recommande également de les déplacer de l'outil de génération et du code de test. Créez votre propre image avec toutes les données dont vous avez besoin ou utilisez AWS CLI pour créer des tables et insérer des données. Suivez le principe de responsabilité unique et les principes d'injection de dépendance: votre code de test ne doit faire que des tests. Tout l'environnement (dans ce cas, des tableaux et des données doivent leur être fournis). Créer une table dans un test est une erreur, car dans la vie réelle, cette table existe déjà (sauf si vous testez une méthode qui crée réellement une table, bien sûr).
Tous les développeurs ont besoin de l'avoir installé
Docker devrait être un must pour tous les développeurs en 2018, donc ce n'est pas un problème.
Et si vous utilisez JUnit 5, il peut être judicieux d’utiliser une extension DynamoDB Local qui injectera le client dans vos tests (oui, je fais une auto-promotion):
Ajoutez le référentiel JCenter à votre construction.
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()
}
Ajouter une dépendance sur 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")
}
Utilisez l'extension dans vos tests:
@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();
}
}
J'ai trouvé que le référentiel Amazon n'avait pas de fichier d'index, il ne semble donc pas fonctionner de manière à vous permettre de l'introduire comme ceci:
maven {
url = "https://s3-us-west-2.amazonaws.com/dynamodb-local/release"
}
Le seul moyen de charger les dépendances est de télécharger DynamoDbLocal en tant que fichier jar et de l'inclure dans mon script de construction, comme suit:
dependencies {
...
runtime files('libs/DynamoDBLocal.jar')
...
}
Bien sûr, cela signifie que toutes les dépendances SQLite et Jetty doivent être importées manuellement. J'essaie toujours de bien comprendre. Si quelqu'un connaît un référentiel fiable pour DynamoDbLocal, j'aimerais vraiment le savoir.
Dans Hadoop, nous utilisons également DynamoDBLocal pour les travaux de test et de débogage. S'il vous plaît voir comment il est utilisé ici comme exemple à: https://github.com/Apache/hadoop/blob/HADOOP-13345/hadoop-tools/hadoop-aws/src/test/Java/org/Apache/hadoop/ fs/s3a/s3guard/TestDynamoDBMetadataStore.Java # L113
Pour les tests unitaires au travail, j'utilise Mockito, puis je me moque de AmazonDynamoDBClient. puis simulez les déclarations en utilisant quand. comme ce qui suit:
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;
}
});
je ne sais pas si c'est ce que vous cherchez, mais c'est comme cela que nous le faisons.
Il existe plusieurs wrappers node.js pour DynamoDB Local. Celles-ci permettent d'exécuter facilement des tests unitaires en combinaison avec des coureurs de tâches comme gulp ou grunt. Essayez dynamodb-localhost , dynamodb-local