Moqueur/stubbing Mangouste modèle méthode save
Donné un simple Mangouste modèle:
import mongoose, { Schema } from 'mongoose';
const PostSchema = Schema({
title: { type: String },
postDate: { type: Date, default: Date.now }
}, { timestamps: true });
const Post = mongoose.model('Post', PostSchema);
export default Post;
Je souhaite tester ce modèle, mais je suis de frapper quelques obstacles.
Mon spec actuelle ressemble à quelque chose comme ceci (certains trucs omis par souci de concision):
import mongoose from 'mongoose';
import { expect } from 'chai';
import { Post } from '../../app/models';
describe('Post', () => {
beforeEach((done) => {
mongoose.connect('mongodb://localhost/node-test');
done();
});
describe('Given a valid post', () => {
it('should create the post', (done) => {
const post = new Post({
title: 'My test post',
postDate: Date.now()
});
post.save((err, doc) => {
expect(doc.title).to.equal(post.title)
expect(doc.postDate).to.equal(post.postDate);
done();
});
});
});
});
Cependant, avec cela, je suis de frapper ma base de données chaque fois que je lance le test, que je préférerais éviter.
J'ai essayé d'utiliser Mockgoose, mais alors mon test ne fonctionne pas.
import mockgoose from 'mockgoose';
//in before or beforeEach
mockgoose(mongoose);
Le test est coincé et renvoyait un message d'erreur disant: Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
j'ai tenté d'augmenter le délai d'attente de 20 secondes, mais ça n'a pas de résoudre quoi que ce soit.
Ensuite, j'ai jeté Mockgoose et essayé d'utiliser Sinon à talon la save
appel.
describe('Given a valid post', () => {
it('should create the post', (done) => {
const post = new Post({
title: 'My test post',
postDate: Date.now()
});
const stub = sinon.stub(post, 'save', function(cb) { cb(null) })
post.save((err, post) => {
expect(stub).to.have.been.called;
done();
});
});
});
Ce test réussit, mais de toute façon il n'a pas beaucoup de sens pour moi. Je suis assez nouveau à écrasant, en se moquant, qu'avez-vous, ... et je ne suis pas sûr si c'est la bonne façon d'aller. Je suis cogner la save
méthode sur post
, et puis je suis en affirmant qu'il a été appelé, mais je suis évidemment en l'appelant... Aussi, je n'arrive pas à obtenir les arguments du non-écrasa Mangouste méthode serait de retour. Je voudrais comparer le post
variable de quelque chose de la save
méthode renvoie, comme dans le tout premier test où j'ai frappé la base de données. J'ai essayé un couple de méthodes mais ils sont tous sentir très à l'hackish. Il doit y avoir un moyen propre, non?
Quelques questions:
-
Dois-je en effet éviter de heurter la base de données comme je l'ai toujours lu partout? Mon premier exemple fonctionne très bien et j'ai pu effacer la base de données après chaque course. Cependant, il ne se sent pas vraiment le droit de me.
-
Comment pourrais-je le talon de la méthode d'enregistrement de la Mangouste modèle et assurez-vous qu'il fait des tests de ce que j'ai envie de tester: l'enregistrement d'un nouvel objet à la db.
- Oleg réponse semble bon si vous êtes un mockist TDDer, mais plus classique TDDers serait n'ai pas de problème avec les coups de la base de données. Pour une explication sur les objets fantaisie, des talons, et mockist vs classique TDD voir Martin Fowler article sur le sujet.
- À la fin de la journée, les tests sont là pour garantir la qualité du code, donc il n'y a pas d'écrire ou de mal, tant que la qualité n'en souffre pas. Les inconvénients de l'unité de base des tests de frapper la DB sont: (1) la vitesse, (2) la complexité de l'IC et de l'individu développeurs du projet, (3) test d'effets secondaires transférés via la DB de l'état, entre les tests individuels ou simultanée de test, qui sont difficiles à résoudre, (4) fixation bug signifie effort supplémentaire pour les développeurs, dans le pire des cas externes de dépendance de la ressource. Il n'y a rien de mal à ce sujet, mais vraiment pour les tests d'intégration uniquement. Je voudrais bien séparer les deux.
- J'ai oublié de mentionner: merci pour le lien à un très intéressant article de Martin Fowler!
- Je suis d'accord avec tout cela. Personnellement, je suis très bien de ne pas avoir de tests unitaires pour la persistance de code et avoir des tests d'intégration uniquement. Cela provient de la "utilisation des objets réels, si possible, la" mentalité de classique TDD. Je voulais juste donner ce point de vue et un peu de fond sur ce à la personne depuis qu'il a dit qu'il est à nouveau à écrasant/moqueur et peuvent ne pas être conscients de tests d'intégration à tous.
Vous devez vous connecter pour publier un commentaire.
Les bases
Dans les tests unitaires, on ne doit pas frapper la DB. J'ai pu penser une seule exception: le fait de frapper une mémoire DB, mais même qui se trouve déjà dans le domaine des tests d'intégration que vous n'aurez besoin que l'état sauvegardé dans la mémoire de processus complexes (et donc pas vraiment unités de fonctionnalité). Donc, oui pas de DB.
Ce que vous voulez de test pour les tests unitaires, c'est que vos affaires de la logique de résultats à corriger les appels de l'API à l'interface entre votre application et la base de données. Vous pouvez et devriez probablement supposer que la DB API/pilote développeurs ont fait un bon travail de test que tout en dessous de l'API se comporte comme prévu. Toutefois, vous aussi vous voulez couvrir dans vos tests comment une logique d'entreprise réagit aux différentes API valide les résultats tels que la réussite de sauve, de défaillances dues à la cohérence des données, des défaillances en raison de problèmes de connexion, etc.
Cela signifie que ce que vous avez besoin et veulent se moquer de tout ce qui est en dessous du pilote de base de données de l'interface. Vous, cependant, besoin de modèle de comportement de sorte que votre logique métier peut être testé pour tous les résultats de la DB appels.
Plus facile à dire qu'à faire car cela signifie que vous devez avoir accès à l'API via la technologie que vous utilisez et vous avez besoin de savoir l'API.
La réalité de la mangouste
Coller à l'essentiel, nous voulons moquer des appels effectués par le sous-jacent 'pilote' qui mangouste utilise. En supposant qu'il est nœud-mongodb-natif nous avons besoin pour se moquer de ces appels. Comprendre l'interaction entre la mangouste et le pilote natif n'est pas facile, mais il s'agit généralement vers le bas pour les méthodes de
mongoose.Collection
parce que ce dernier s'étendmongoldb.Collection
et ne pas réimplémenter des méthodes commeinsert
. Si nous sommes en mesure de contrôler le comportement deinsert
dans ce cas particulier, alors nous savons que nous moqué de la DB accès au niveau de l'API. Vous pouvez remonter à la source de ces deux projets, quiCollection.insert
est vraiment le pilote natif de la méthode.Pour votre exemple, j'ai créé un public dépôt Git avec un paquet complet, mais je vais poster tous les éléments ici dans la réponse.
La solution
Personnellement, je trouve le "recommandé" à la façon de travailler avec la mangouste tout à fait inutilisable: les modèles sont généralement créés dans les modules où l'schémas sont définis, mais ils ont déjà besoin d'une connexion. Pour les fins d'avoir plusieurs connexions de parler complètement différentes bases de données mongodb dans le même projet et pour des fins de test, ce qui rend la vie vraiment difficile. En fait, dès que les préoccupations sont entièrement séparés de la mangouste, au moins pour moi, devient presque inutilisable.
Donc, la première chose que j'ai créer est la description du package de fichier, un module avec un schéma et un générique de "modèle générateur":
Un tel générateur de modèle a ses inconvénients: il y a des éléments qui peuvent avoir besoin d'être attaché au modèle, et il serait judicieux de les placer dans le même module, où le schéma est créé. Donc, trouver un moyen générique pour ajouter ces est un peu délicat. Par exemple, un module pourrait exporter de l'après-actions à exécuter automatiquement lorsqu'un modèle est généré pour une connexion donnée, etc. (le piratage).
Maintenant, nous allons simuler l'API. Je vais rester simple et ne se moquer de ce dont j'ai besoin pour les tests en question. Il est essentiel que je voudrais pour se moquer de l'API en général, et non les méthodes de différentes instances. Ce dernier peut être utile dans certains cas, ou lorsque rien ne vient en aide, mais j'aurais besoin d'avoir accès à des objets créés à l'intérieur de ma logique métier (sauf injecté ou fournis par le biais de certains modèle de fabrique), et cela signifie la modification de la source principale. Dans le même temps, se moquant de l'API dans un endroit a un inconvénient: c'est une solution générique, ce qui serait probablement une mise en œuvre réussie de l'exécution. Pour tester les cas d'erreur, les moqueries dans les cas les tests eux-mêmes pourraient être requis, mais dans une logique d'entreprise, vous pourriez ne pas avoir un accès direct à l'instance, par exemple, de
post
créé à l'intérieur profond.Donc, nous allons jeter un coup d'oeil au cas général de se moquer de succès de l'appel API:
En général, aussi longtemps que les modèles sont créés après la modification de la mangouste, il est envisageable que le ci-dessus se moque sont fait par test de base pour simuler les comportements. Assurez-vous de revenir à l'origine du comportement, cependant, avant chaque test!
Enfin, c'est la façon dont nos tests pour tous les possible de sauvegarder les données des opérations pourrait ressembler. Attention, ce ne sont pas spécifiques à notre
Post
modèle et pourrait être fait pour tous les autres modèles avec exactement la même maquette en place.Il est essentiel de noter que nous sommes encore en test le très faible niveau des fonctionnalités, mais nous pouvons utiliser cette même approche pour tester toute la logique d'entreprise qui utilise
Post.create
oupost.save
en interne.La finale très peu, nous allons exécuter les tests:
Je dois dire, ce n'est pas drôle de le faire de cette façon. Mais de cette façon c'est vraiment du pur tests unitaires de la logique métier sans en mémoire ou de la vraie et de la DBs assez générique.
mongoose
des trucs sans se connecter au réeldb
. Est la solution ci-dessus vraimentdb
isolé? cevar conn = mongoose.createConnection();
est en train de faire là?Si ce que vous voulez, c'est de tester
static's
etmethod's
de certains Mangouste modèle, je vous recommande d'utiliser sinon et sinon-mangouste. (Je suppose que c'est compatible avec chai)De cette façon, vous n'aurez pas besoin de vous connecter à Mongo DB.
Suivant votre exemple, supposons que vous avez une méthode statique
findLast
Ensuite, pour tester cette méthode
Vous pouvez trouver de travail (et simple) des exemples sur le sinon-mangouste repo.