Ця відповідь заснована на відповіді Андреаса Кеберле .
Мені було не так просто реалізувати та зрозуміти його рішення, тому я поясню його трохи детальніше, як воно працює, та деякі підводні камені, яких слід уникати, сподіваючись, що це допоможе майбутнім відвідувачам.
Отже, насамперед налаштування:
я використовую Karma в якості тестового бігуна та MochaJs як тестовий фреймворк.
Використовуючи щось на зразок Squire , у мене не вийшло, чомусь, коли я ним користувався, тестовий фреймворк викинув помилки:
TypeError: Неможливо прочитати властивість 'call' невизначеного
RequireJs має можливість зіставити ідентифікатори модуля на інші ідентифікатори модуля. Це також дозволяє створити requireфункцію, яка використовує інший конфігурацію, ніж глобальна require.
Ці функції мають вирішальне значення для роботи цього рішення.
Ось моя версія мок-коду, включаючи (багато) коментарів (сподіваюся, це зрозуміло). Я загорнув його всередину модуля, щоб тести могли його легко вимагати.
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;
});
Велика пастка я зіткнувся, що буквально коштувало мені годин, створював RequireJs конфігурації. Я спробував (глибоко) скопіювати його та лише перекреслити необхідні властивості (наприклад, контекст чи карту). Це не працює! Тільки скопіюйте baseUrl, це добре працює.
Використання
Щоб скористатись цим, зажадайте його у вашому тесті, створіть макети, а потім передайте їх createMockRequire. Наприклад:
var ModuleMock = function () {
this.method = function () {
methodCalled += 1;
};
};
var mocks = {
"ModuleIdOrPath": ModuleMock
}
var requireMocks = mocker.createMockRequire(mocks);
І ось приклад повного тестового файлу :
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функцію. Однак є кілька різних варіантів. Я опублікую відповідь, сподіваючись, що вона буде корисною.