Ceci est un exemple trivial qui illustre le nœud de mon problème:
var innerLib = require('./path/to/innerLib');
function underTest() {
return innerLib.doComplexStuff();
}
module.exports = underTest;
J'essaye d'écrire un test unitaire pour ce code. Comment puis-je simuler l'exigence de la innerLib
sans se moquer complètement de la require
fonction?
Donc, c'est moi qui essaie de me moquer du monde require
et de découvrir que cela ne fonctionnera même pas pour faire cela:
var path = require('path'),
vm = require('vm'),
fs = require('fs'),
indexPath = path.join(__dirname, './underTest');
var globalRequire = require;
require = function(name) {
console.log('require: ' + name);
switch(name) {
case 'connect':
case indexPath:
return globalRequire(name);
break;
}
};
Le problème est que la require
fonction à l'intérieur du underTest.js
fichier n'a en fait pas été simulée. Il pointe toujours vers la require
fonction globale . Il semble donc que je ne puisse simuler la require
fonction que dans le même fichier dans lequel je me moque. Si j'utilise le global require
pour inclure quoi que ce soit, même après avoir remplacé la copie locale, les fichiers requis auront toujours le require
référence mondiale .
la source
global.require
. Les variables écritesmodule
par défaut car les modules ont une portée de module.Réponses:
Tu peux maintenant!
J'ai publié proxyquire qui se chargera de remplacer le requis global dans votre module pendant que vous le testez.
Cela signifie que vous n'avez pas besoin de modifier votre code pour injecter des simulations pour les modules requis.
Proxyquire a une API très simple qui permet de résoudre le module que vous essayez de tester et de transmettre des simulacres / stubs pour ses modules requis en une seule étape.
@Raynos a raison de dire que traditionnellement, vous deviez recourir à des solutions pas très idéales pour y parvenir ou faire du développement ascendant à la place
C'est la raison principale pour laquelle j'ai créé proxyquire - pour permettre un développement de haut en bas piloté par des tests sans aucun problème.
Jetez un œil à la documentation et aux exemples afin de déterminer s'il répondra à vos besoins.
la source
proxyquire
!Une meilleure option dans ce cas est de simuler les méthodes du module renvoyé.
Pour le meilleur ou pour le pire, la plupart des modules node.js sont des singletons; deux morceaux de code qui nécessitent () le même module obtiennent la même référence à ce module.
Vous pouvez en tirer parti et utiliser quelque chose comme sinon pour simuler les éléments requis. Le test de moka suit:
Sinon a une bonne intégration avec chai pour faire des assertions, et j'ai écrit un module pour intégrer sinon avec mocha pour permettre un nettoyage plus facile des espions / stub (pour éviter la pollution des tests.)
Notez que underTest ne peut pas être simulé de la même manière, car underTest ne renvoie qu'une fonction.
Une autre option consiste à utiliser des simulations Jest. Suivez leur page
la source
require('some_module')
, car tout votre code partage le même répertoire node_modules. Deuxièmement, l'article confond l'espace de noms avec des singletons, ce qui est en quelque sorte orthogonal. Troisièmement, cet article est sacrément vieux (en ce qui concerne node.js), donc ce qui aurait pu être valide à l'époque n'est peut-être pas valide maintenant.innerLib.toCrazyCrap.restore()
et restub, ou appelez via sinon cesinon.stub(innerLib, 'toCrazyCrap')
qui vous permet de changer la façon dont les stub se comporte:innerLib.toCrazyCrap.returns(false)
. De plus, rewire semble être très similaire à l'proxyquire
extension ci-dessus.j'utilise mock-require . Assurez-vous de définir vos simulacres avant
require
le module à tester.la source
Se moquer
require
me semble être un vilain hack. J'essaierais personnellement de l'éviter et de refactoriser le code pour le rendre plus testable. Il existe différentes approches pour gérer les dépendances.1) Passer les dépendances comme arguments
Cela rendra le code testable universellement. L'inconvénient est que vous devez passer des dépendances, ce qui peut rendre le code plus compliqué.
2) implémentez le module en tant que classe, puis utilisez les méthodes / propriétés de classe pour obtenir les dépendances
(Ceci est un exemple artificiel, où l'utilisation de classe n'est pas raisonnable, mais elle transmet l'idée) (exemple ES6)
Vous pouvez maintenant facilement
getInnerLib
tester la méthode stub pour tester votre code. Le code devient plus détaillé, mais aussi plus facile à tester.la source
colors
module populaire quiString.prototype
Si vous avez déjà utilisé jest, vous êtes probablement familier avec la fonction simulée de jest.
En utilisant "jest.mock (...)", vous pouvez simplement spécifier la chaîne qui apparaîtrait dans une instruction require dans votre code quelque part et chaque fois qu'un module est requis en utilisant cette chaîne, un objet-maquette serait renvoyé à la place.
Par exemple
remplacerait complètement toutes les importations / demandes de "firebase-admin" par l'objet que vous avez renvoyé de cette fonction "usine".
Eh bien, vous pouvez le faire lorsque vous utilisez jest car jest crée un runtime autour de chaque module qu'il exécute et injecte une version "accrochée" de require dans le module, mais vous ne pourriez pas le faire sans plaisanter.
J'ai essayé d'y parvenir avec mock-require, mais pour moi, cela ne fonctionnait pas pour les niveaux imbriqués dans ma source. Jetez un œil au problème suivant sur github: mock-require pas toujours appelé avec Mocha .
Pour résoudre ce problème, j'ai créé deux modules npm que vous pouvez utiliser pour réaliser ce que vous voulez.
Vous avez besoin d'un babel-plugin et d'un module mocker.
Dans votre .babelrc, utilisez le plugin babel-plugin-mock-require avec les options suivantes:
et dans votre fichier de test, utilisez le module jestlike-mock comme ceci:
Le
jestlike-mock
module est encore très rudimentaire et n'a pas beaucoup de documentation mais il n'y a pas beaucoup de code non plus. J'apprécie tous les PR pour un ensemble de fonctionnalités plus complet. Le but serait de recréer toute la fonctionnalité "jest.mock".Afin de voir comment jest implémente cela, on peut rechercher le code dans le package "jest-runtime". Voir https://github.com/facebook/jest/blob/master/packages/jest-runtime/src/index.js#L734 par exemple, ici ils génèrent un "automock" d'un module.
J'espère que cela pourra aider ;)
la source
Vous ne pouvez pas. Vous devez construire votre suite de tests unitaires afin que les modules les plus bas soient testés en premier et que les modules de niveau supérieur qui nécessitent des modules soient testés ensuite.
Vous devez également supposer que tout code tiers et node.js lui-même sont bien testés.
Je suppose que vous verrez arriver dans un proche avenir des frameworks moqueurs qui écrasent
global.require
Si vous devez vraiment injecter une maquette, vous pouvez modifier votre code pour exposer une portée modulaire.
Soyez averti que cela expose
.__module
dans votre API et tout code peut accéder à la portée modulaire à son propre danger.la source
Vous pouvez utiliser la bibliothèque de moquerie :
la source
Code simple pour simuler des modules pour les curieux
Remarquez les parties où vous manipulez la méthode
require.cache
et noterequire.resolve
car c'est la sauce secrète.Utilisez comme :
MAIS ... proxyquire est assez génial et vous devriez l'utiliser. Il maintient vos exigences de remplacement localisées aux tests uniquement et je le recommande vivement.
la source