Questa risposta si basa sulla risposta di Andreas Köberle .
Non è stato facile per me implementare e comprendere la sua soluzione, quindi lo spiegherò un po 'più in dettaglio come funziona e alcune insidie da evitare, sperando che possa aiutare i futuri visitatori.
Quindi, prima di tutto l'installazione:
sto usando Karma come test runner e MochaJs come framework di test.
Usare qualcosa come Squire non ha funzionato per me, per qualche ragione, quando l'ho usato, il framework di test ha generato errori:
TypeError: impossibile leggere la proprietà 'call' di undefined
RequireJs ha la possibilità di mappare gli id del modulo su altri ID del modulo. Inoltre, consente di creare una require
funzione che utilizza una configurazione diversa da quella globale require
.
Queste funzionalità sono fondamentali per il funzionamento di questa soluzione.
Ecco la mia versione del codice simulato, inclusi (molti) commenti (spero che sia comprensibile). L'ho avvolto in un modulo, in modo che i test possano facilmente richiederlo.
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;
});
La più grande trappola che ho incontrato, che mi è costato letteralmente ore, è stata la creazione della configurazione di RequireJs. Ho provato a copiarlo (in profondità) e ho solo la precedenza sulle proprietà necessarie (come il contesto o la mappa). Questo non funziona! Copia solo il baseUrl
, questo funziona bene.
uso
Per usarlo, richiedilo nel tuo test, crea le beffe e poi passalo a createMockRequire
. Per esempio:
var ModuleMock = function () {
this.method = function () {
methodCalled += 1;
};
};
var mocks = {
"ModuleIdOrPath": ModuleMock
}
var requireMocks = mocker.createMockRequire(mocks);
E qui un esempio di un file di test completo :
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
funzione. Ci sono alcune diverse opzioni però. Pubblicherò una risposta nella speranza che possa essere utile.