Esta respuesta se basa en la respuesta de Andreas Köberle .
No fue tan fácil para mí implementar y comprender su solución, por lo que lo explicaré con un poco más de detalle cómo funciona, y algunas dificultades para evitar, con la esperanza de que ayude a futuros visitantes.
Entonces, primero que nada la configuración:
estoy usando Karma como corredor de prueba y MochaJs como marco de prueba.
Usar algo como Squire no funcionó para mí, por alguna razón, cuando lo usé, el marco de prueba arrojó errores:
TypeError: no se puede leer la propiedad 'call' de undefined
RequireJs tiene la posibilidad de asignar identificadores de módulo a otros identificadores de módulo. También permite crear una require
función que utiliza una configuración diferente a la global require
.
Estas características son cruciales para que esta solución funcione.
Aquí está mi versión del código simulado, que incluye (muchos) comentarios (espero que sea comprensible). Lo envolví dentro de un módulo, para que las pruebas puedan requerirlo fácilmente.
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;
});
El mayor obstáculo que encontré, que literalmente me costó horas, fue crear la configuración RequireJs. Traté de copiarlo (en profundidad) y solo anular las propiedades necesarias (como contexto o mapa). ¡Esto no funciona! Solo copie el baseUrl
, esto funciona bien.
Uso
Para usarlo, solicítelo en su prueba, cree los simulacros y luego páselo createMockRequire
. Por ejemplo:
var ModuleMock = function () {
this.method = function () {
methodCalled += 1;
};
};
var mocks = {
"ModuleIdOrPath": ModuleMock
}
var requireMocks = mocker.createMockRequire(mocks);
Y aquí un ejemplo de un archivo de prueba 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
función. Sin embargo, hay algunas opciones diferentes. Publicaré una respuesta con la esperanza de que sea útil.