Cette réponse est basée sur la réponse d'Andreas Köberle .
Ce n'était pas si simple pour moi d'implémenter et de comprendre sa solution, je vais donc vous expliquer un peu plus en détail comment cela fonctionne, et quelques écueils à éviter, en espérant que cela aidera les futurs visiteurs.
Donc, tout d'abord la configuration:
j'utilise Karma comme testeur et MochaJs comme cadre de test.
Utiliser quelque chose comme Squire ne fonctionnait pas pour moi, pour une raison quelconque, lorsque je l'ai utilisé, le cadre de test a généré des erreurs:
TypeError: impossible de lire la propriété 'call' de undefined
RequireJs a la possibilité de mapper les identifiants de module à d'autres identifiants de module. Il permet également de créer une require
fonction qui utilise une configuration différente de celle du global require
.
Ces fonctionnalités sont essentielles pour que cette solution fonctionne.
Voici ma version du code fictif, y compris (beaucoup) de commentaires (j'espère que c'est compréhensible). Je l'ai enveloppé dans un module, pour que les tests puissent facilement l'exiger.
define([], function () {
var count = 0;
var requireJsMock= Object.create(null);
requireJsMock.createMockRequire = function (mocks) {
//mocks is an object with the module ids/paths as keys, and the module as value
count++;
var map = {};
//register the mocks with unique names, and create a mapping from the mocked module id to the mock module id
//this will cause RequireJs to load the mock module instead of the real one
for (property in mocks) {
if (mocks.hasOwnProperty(property)) {
var moduleId = property; //the object property is the module id
var module = mocks[property]; //the value is the mock
var stubId = 'stub' + moduleId + count; //create a unique name to register the module
map[moduleId] = stubId; //add to the mapping
//register the mock with the unique id, so that RequireJs can actually call it
define(stubId, function () {
return module;
});
}
}
var defaultContext = requirejs.s.contexts._.config;
var requireMockContext = { baseUrl: defaultContext.baseUrl }; //use the baseUrl of the global RequireJs config, so that it doesn't have to be repeated here
requireMockContext.context = "context_" + count; //use a unique context name, so that the configs dont overlap
//use the mapping for all modules
requireMockContext.map = {
"*": map
};
return require.config(requireMockContext); //create a require function that uses the new config
};
return requireJsMock;
});
Le plus gros piège que j'ai rencontré, qui m'a coûté des heures, a été la création de la configuration RequireJs. J'ai essayé de le copier (en profondeur) et de ne remplacer que les propriétés nécessaires (comme le contexte ou la carte). Cela ne fonctionne pas! Copiez uniquement lebaseUrl
, cela fonctionne très bien.
Usage
Pour l'utiliser, exigez-le dans votre test, créez les simulacres, puis transmettez-le à createMockRequire
. Par exemple:
var ModuleMock = function () {
this.method = function () {
methodCalled += 1;
};
};
var mocks = {
"ModuleIdOrPath": ModuleMock
}
var requireMocks = mocker.createMockRequire(mocks);
Et voici un exemple de fichier de test complet :
define(["chai", "requireJsMock"], function (chai, requireJsMock) {
var expect = chai.expect;
describe("Module", function () {
describe("Method", function () {
it("should work", function () {
return new Promise(function (resolve, reject) {
var handler = { handle: function () { } };
var called = 0;
var moduleBMock = function () {
this.method = function () {
methodCalled += 1;
};
};
var mocks = {
"ModuleBIdOrPath": moduleBMock
}
var requireMocks = requireJsMock.createMockRequire(mocks);
requireMocks(["js/ModuleA"], function (moduleA) {
try {
moduleA.method(); //moduleA should call method of moduleBMock
expect(called).to.equal(1);
resolve();
} catch (e) {
reject(e);
}
});
});
});
});
});
});
define
fonction. Il existe cependant quelques options différentes. Je posterai une réponse dans l'espoir qu'elle vous sera utile.