Question Comment tester un module node.js qui nécessite d'autres modules


Ceci est un exemple trivial qui illustre l'essentiel de mon problème:

var innerLib = require('./path/to/innerLib');

function underTest() {
    return innerLib.doComplexStuff();
}

module.exports = underTest;

J'essaie d'écrire un test unitaire pour ce code. Comment puis-je simuler l'exigence de la innerLib sans se moquer de la require fonctionne entièrement?

EDIT: Donc, c’est moi qui essaie de simuler le besoin global et de découvrir que cela ne marchera même pas:

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 le require la fonction dans le fichier underTest.js n'a en fait pas été simulée. Il pointe toujours vers le global require fonction. Donc, il semble que je ne peux que simuler le require fonction dans le même fichier je fais le moqueur. Si j'utilise le global require pour inclure quelque chose, même après avoir remplacé la copie locale, les fichiers requis auront toujours require référence.


129
2018-04-21 16:30


origine


Réponses:


Tu peux maintenant!

J'ai publié proxyquire qui prendra soin de remplacer le besoin global dans votre module pendant que vous le testez.

Cela signifie que vous avez besoin aucun changement à votre code afin d'injecter des mock pour les modules requis.

Proxyquire a un api très simple qui permet de résoudre le module que vous essayez de tester et de transmettre des simulations / stubs pour ses modules requis en une seule étape.

@Raynos a raison de dire que, traditionnellement, vous deviez recourir à des solutions peu idéales pour y parvenir ou faire un développement ascendant au lieu de cela.

Ce qui est la raison principale pour laquelle j'ai créé proxyquire - pour permettre un développement piloté de haut en bas sans aucun problème.

Jetez un coup d’œil à la documentation et aux exemples afin de déterminer si elle répondra à vos besoins.


157
2017-08-02 02:20



Une meilleure option dans ce cas est de simuler des 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 nécessitant () le même module obtiennent la même référence à ce module.

Vous pouvez tirer parti de cela et utiliser quelque chose comme sinon pour simuler les éléments requis. moka test suit:

// in your testfile
var innerLib  = require('./path/to/innerLib');
var underTest = require('./path/to/underTest');
var sinon     = require('sinon');

describe("underTest", function() {
  it("does something", function() {
    sinon.stub(innerLib, 'toCrazyCrap', function() {
      // whatever you would like innerLib.toCrazyCrap to do under test
    });

    underTest();

    sinon.assert.calledOnce(innerLib.toCrazyCrap); // sinon assertion

    innerLib.toCrazyCrap.restore(); // restore original functionality
  });
});

Sinon a bien intégration avec chai pour faire des assertions, et j'ai écrit un module pour intégrer sinon avec moka pour faciliter le nettoyage d'espionnage / espacement (pour éviter la pollution de test).

Notez que underTest ne peut pas être simulé de la même manière, car underTest ne renvoie qu'une fonction.


98
2018-04-30 20:04



j'utilise besoin simulé. Assurez-vous de définir vos mocks devant vous require le module à tester.


7
2017-07-14 16:48



Vous pouvez utiliser moquerie bibliothèque:

describe 'UnderTest', ->
  before ->
    mockery.enable( warnOnUnregistered: false )
    mockery.registerMock('./path/to/innerLib', { doComplexStuff: -> 'Complex result' })
    @underTest = require('./path/to/underTest')

  it 'should compute complex value', ->
    expect(@underTest()).to.eq 'Complex result'

1
2017-11-15 11:38



Vous ne pouvez pas. Vous devez construire votre suite de test unitaire pour que les modules les plus bas soient testés en premier et que les modules de niveau supérieur nécessitant des modules soient testés par la suite.

Vous devez également supposer que tout code tiers et node.js est bien testé.

Je suppose que vous verrez des cadres moqueurs arriver dans un avenir proche qui écrasent global.require

Si vous devez vraiment injecter une maquette, vous pouvez modifier votre code pour exposer la portée modulaire.

// underTest.js
var innerLib = require('./path/to/innerLib');

function underTest() {
    return innerLib.toCrazyCrap();
}

module.exports = underTest;
module.exports.__module = module;

// test.js
function test() {
    var underTest = require("underTest");
    underTest.__module.innerLib = {
        toCrazyCrap: function() { return true; }
    };
    assert.ok(underTest());
}

Soyez averti que cela expose .__module dans votre API et tout code peut accéder à la portée modulaire à leur propre danger.


0
2018-04-21 16:47



Railleur require se sent comme un hack méchant à moi. 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 des dépendances comme arguments

function underTest(innerLib) {
    return innerLib.doComplexStuff();
}

Cela rendra le code universellement testable. L'inconvénient est que vous devez transmettre des dépendances, ce qui peut rendre le code plus compliqué.

2) implémenter le module en tant que classe, puis utiliser des méthodes / propriétés de classe pour obtenir des dépendances

(Ceci est un exemple artificiel, où l'utilisation de la classe n'est pas raisonnable, mais elle transmet l'idée) (Exemple ES6)

const innerLib = require('./path/to/innerLib')

class underTestClass {
    getInnerLib () {
        return innerLib
    }

    underTestMethod () {
        return this.getInnerLib().doComplexStuff()
    }
}

Maintenant, vous pouvez facilement tronquer getInnerLib méthode pour tester votre code. Le code devient plus verbeux, mais aussi plus facile à tester.


0
2017-12-12 15:38