Diese Antwort basiert auf der Antwort von Andreas Köberle .
Es war nicht so einfach für mich, seine Lösung zu implementieren und zu verstehen, deshalb werde ich sie etwas detaillierter erklären, wie sie funktioniert, und einige Fallstricke vermeiden, in der Hoffnung, dass sie zukünftigen Besuchern helfen wird.
Also zuerst das Setup:
Ich benutze Karma als Testläufer und MochaJs als Testframework .
Die Verwendung von Squire hat bei mir aus irgendeinem Grund nicht funktioniert. Als ich es verwendete, warf das Testframework Fehler auf:
TypeError: Die Eigenschaft 'call' von undefined kann nicht gelesen werden
RequireJs bietet die Möglichkeit, Modul-IDs anderen Modul-IDs zuzuordnen . Außerdem können Sie eine require
Funktion erstellen , die eine andere Konfiguration als die globale verwendet require
.
Diese Funktionen sind für das Funktionieren dieser Lösung von entscheidender Bedeutung.
Hier ist meine Version des Scheincodes, einschließlich (vieler) Kommentare (ich hoffe, es ist verständlich). Ich habe es in ein Modul eingewickelt, damit die Tests es leicht erfordern können.
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;
});
Die größte Gefahr, auf die ich gestoßen bin und die mich buchstäblich Stunden gekostet hat, war das Erstellen der RequireJs-Konfiguration. Ich habe versucht, es (tief) zu kopieren und nur die erforderlichen Eigenschaften (wie Kontext oder Karte) zu überschreiben. Das funktioniert nicht! Kopieren Sie nur die baseUrl
, das funktioniert gut.
Verwendung
Um es zu verwenden, benötigen Sie es in Ihrem Test, erstellen Sie die Mocks und übergeben Sie es dann an createMockRequire
. Beispielsweise:
var ModuleMock = function () {
this.method = function () {
methodCalled += 1;
};
};
var mocks = {
"ModuleIdOrPath": ModuleMock
}
var requireMocks = mocker.createMockRequire(mocks);
Und hier ein Beispiel für eine vollständige Testdatei :
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
Funktion zu verspotten . Es gibt jedoch einige verschiedene Optionen. Ich werde eine Antwort in der Hoffnung posten, dass es hilfreich sein wird.