Tests unitaires avec MongoDB

Ma firebase database de choix est MongoDB. J’écris une API de couche de données pour résumer les détails d’implémentation à partir des applications clientes – c’est-à-dire que je fournis essentiellement une interface publique unique (un object qui agit comme un IDL).

Je teste ma logique au fur et à mesure que je progresse. Avant chaque test unitaire, une méthode @Before est appelée pour créer un singleton de firebase database, après quoi, une fois le test terminé, une méthode @After est appelée pour supprimer la firebase database. Cela aide à promouvoir l’indépendance entre les tests unitaires.

Presque tous les tests unitaires, c’est- à- dire l’ exécution d’une requête contextuelle , nécessitent une sorte de logique d’insertion avant la main. Mon interface publique fournit une méthode d’insertion – pourtant, il semble incorrect d’utiliser cette méthode comme logique précurseur pour chaque test unitaire.

Vraiment, j’ai besoin d’une sorte de mécanisme moqueur, pourtant, je n’ai pas beaucoup d’expérience avec les frameworks moqueurs, et il semble que Google ne retourne rien de ce qui pourrait être utilisé avec MongoDB.

Que font les autres dans ces situations? C’est-à-dire, comment un code de test d’unité interagit avec une firebase database?

De plus, mon interface publique se connecte à une firebase database définie dans un fichier de configuration externe – il semble incorrect d’utiliser cette connexion pour mes tests unitaires – encore une fois, une situation qui pourrait être moqueuse?

Comme sbridges l’a écrit dans cet article, il est une mauvaise idée de ne pas avoir un service dédié (parfois aussi appelé référentiel ou DAO) qui extrait l’access aux données de la logique. Ensuite, vous pouvez tester la logique en fournissant une maquette du DAO.

Une autre approche que je fais est de créer une maquette de l’object Mongo (par exemple PowerMockito), puis de renvoyer les résultats appropriés. Ceci parce que vous n’avez pas à tester si la firebase database fonctionne dans les tests unitaires, mais plus vous devriez tester si la requête correcte a été envoyée à la firebase database.

 Mongo mongo = PowerMockito.mock(Mongo.class); DB db = PowerMockito.mock(DB.class); DBCollection dbCollection = PowerMockito.mock(DBCollection.class); PowerMockito.when(mongo.getDB("foo")).thenReturn(db); PowerMockito.when(db.getCollection("bar")).thenReturn(dbCollection); MyService svc = new MyService(mongo); // Use some kind of dependency injection svc.getObjectById(1); PowerMockito.verify(dbCollection).findOne(new BasicDBObject("_id", 1)); 

Ce serait aussi une option. Bien sûr, la création des simulacres et le retour des objects appropriés sont simplement codés comme un exemple ci-dessus.

Techniquement, les tests qui parlent à une firebase database (nosql ou autre) ne sont pas des tests unitaires , car ils testent les interactions avec un système externe et ne testent pas uniquement une unité de code isolée. Cependant, les tests qui parlent à une firebase database sont souvent extrêmement utiles et sont souvent assez rapides pour être exécutés avec les autres tests unitaires.

En général, j’ai une interface de service (par exemple, UserService) qui encapsule toute la logique de traitement de la firebase database. Le code qui repose sur UserService peut utiliser une version simulée de UserService et est facilement testé.

Lorsque vous testez l’implémentation du service qui parle à Mongo (par exemple, MongoUserService), il est plus facile d’écrire du code Java qui va démarrer / arrêter un processus mongo sur la machine locale. notes .

Vous pouvez essayer de vous moquer de la fonctionnalité de la firebase database en testant MongoUserService, mais en général, cette méthode est trop sujette aux erreurs et ne teste pas ce que vous voulez vraiment tester, à savoir l’interaction avec une firebase database réelle. Ainsi, lorsque vous écrivez des tests pour MongoUserService, vous définissez un état de firebase database pour chaque test. Regardez DbUnit pour un exemple de framework pour le faire avec une firebase database.

J’ai écrit une implémentation de stub MongoDB en Java: mongo-java-server

Default est un backend en mémoire, qui peut être facilement utilisé dans les tests Unit et Integration.

Exemple

 MongoServer server = new MongoServer(new MemoryBackend()); // bind on a random local port InetSocketAddress serverAddress = server.bind(); MongoClient client = new MongoClient(new ServerAddress(serverAddress)); DBCollection coll = client.getDB("testdb").getCollection("testcoll"); // creates the database and collection in memory and inserts the object coll.insert(new BasicDBObject("key", "value")); assertEquals(1, collection.count()); assertEquals("value", collection.findOne().get("key")); client.close(); server.shutdownNow(); 

Je suis surpris que personne n’ait conseillé d’utiliser fakemongo jusqu’à présent. Il émule assez bien le client mongo, et tout fonctionne sur la même JVM avec les tests – les tests d’intégration deviennent donc robustes et techniquement beaucoup plus proches des vrais “tests unitaires”, car aucune interaction de système étranger n’a lieu. C’est comme utiliser H2 embarqué pour tester votre code SQL. J’ai été très heureux d’utiliser fakemongo dans les tests unitaires qui testent le code d’intégration de la base de manière complète. Considérez cette configuration dans le contexte du test print:

 @Configuration @Slf4j public class FongoConfig extends AbstractMongoConfiguration { @Override public Ssortingng getDatabaseName() { return "mongo-test"; } @Override @Bean public Mongo mongo() throws Exception { log.info("Creating Fake Mongo instance"); return new Fongo("mongo-test").getMongo(); } @Bean @Override public MongoTemplate mongoTemplate() throws Exception { return new MongoTemplate(mongo(), getDatabaseName()); } } 

Avec cela, vous pouvez tester votre code qui utilise MongoTemplate à partir du contexte Spring, et en combinaison avec nosql-unit , jsonunit , etc., vous obtenez des tests unitaires robustes qui couvrent le code d’interrogation mongo.

 @Test @UsingDataSet(locations = {"/TSDR1326-data/TSDR1326-subject.json"}, loadStrategy = LoadStrategyEnum.CLEAN_INSERT) @DatabaseSetup({"/TSDR1326-data/dbunit-TSDR1326.xml"}) public void shouldCleanUploadSubjectCollection() throws Exception { //given JobParameters jobParameters = new JobParametersBuilder() .addSsortingng("studyId", "TSDR1326") .addSsortingng("execId", UUID.randomUUID().toSsortingng()) .toJobParameters(); //when //next line runs a Spring Batch ETL process loading data from SQL DB(H2) into Mongo final JobExecution res = jobLauncherTestUtils.launchJob(jobParameters); //then assertThat(res.getExitStatus()).isEqualTo(ExitStatus.COMPLETED); final Ssortingng resultJson = mongoTemplate.find(new Query().with(new Sort(Sort.Direction.ASC, "topLevel.subjectId.value")), DBObject.class, "subject").toSsortingng(); assertThatJson(resultJson).isArray().ofLength(3); assertThatDateNode(resultJson, "[0].topLevel.timestamp.value").isEqualTo(res.getStartTime()); assertThatNode(resultJson, "[0].topLevel.subjectECode.value").isSsortingngEqualTo("E01"); assertThatDateNode(resultJson, "[0].topLevel.subjectECode.timestamp").isEqualTo(res.getStartTime()); ... etc } 

J’ai utilisé fakemongo sans problème avec le pilote mongo 3.4, et la communauté est vraiment proche de publier une version qui prend en charge le pilote 3.6 ( https://github.com/fakemongo/fongo/issues/316 ).