#javascript #jestjs
#javascript #jestjs
Вопрос:
Я хочу издеваться над Math.random для определенных тестов и использовать его оригинальную реализацию для других тестов. Как я могу этого добиться? Я читал об использовании jest.doMock
и jest.dontMock
, но я столкнулся с рядом проблем, используя их, например:
- Кажется, мне нужно
require
использоватьdoMock
иdontMock
, но мой проект использует только модули ES6 для импорта модулей - У этих функций также есть проблемы с использованием глобального модуля, например
Math
. Я получаю сообщение об ошибке при попытке использованияjest.doMock("Math.random")
, что приводит кCannot find module 'Math' from 'app.test.js'
Мне не обязательно использовать doMock
and dontMock
для моих тестов. Они просто казались самым близким, что я мог найти в документации jest к тому, чего я хочу достичь. Но я открыт для альтернативных решений.
Мою функцию я хочу протестировать внутри app.js …
export function getRandomId(max) {
if (!Number.isInteger(max) || max <= 0) {
throw new TypeError("Max is an invalid type");
}
return Math.floor(Math.random() * totalNumPeople) 1;
}
Внутри app.test.js …
describe("getRandomId", () => {
const max = 10;
Math.random = jest.fn();
test("Minimum value for an ID is 1", () => {
Math.mockImplementationOnce(() => 0);
const id = app.getRandomId(max);
expect(id).toBeGreaterThanOrEqual(1);
});
test("Error thrown for invalid argument", () => {
// I want to use the original implementation of Math.random here
expect(() => getRandomId("invalid")).toThrow();
})
});
Комментарии:
1. Вы смотрели на jestjs.io/docs/en/mock-function-api#mockfnmockreset и jestjs.io/docs/en/mock-functions#mocking-modules ?
Ответ №1:
Попробуйте это:
describe("getRandomId", () => {
const max = 10;
let randomMock;
beforeEach(() => {
randomMock = jest.spyOn(global.Math, 'random');
});
test("Minimum value for an ID is 1", () => {
randomMock.mockReturnValue(0);
const id = getRandomId(max);
expect(id).toBeGreaterThanOrEqual(1);
});
test("Error thrown for invalid argument", () => {
// I want to use the original implementation of Math.random here
randomMock.mockRestore(); // restores the original (non-mocked) implementation
expect(() => getRandomId("invalid")).toThrow();
})
});
Комментарии:
1. Спасибо, это было очень полезно! Однако я заметил странное поведение при использовании
mockRestore
во втором тесте. Из любопытства я хотел посмотреть, чтоMath.random
вернулось после восстановленияrandomMock
. Итак, я поместил aexpect(randomMock).toHaveReturnedWith(20)
, чтобы сделать тест неудачным и посмотреть, чтоMath.Random
на самом деле вернулось. Как ни странно, тест не удался, потомуMath.Random
что вообще не вызывался.2. Если я вместо этого использую
mockReturnValueOnce(0)
для первого теста и заменяюmockRestore
наmockClear
во втором тесте, проблема решена. Результаты показывают, чтоMath.Random
он вызывается с его первоначальной реализацией. Вы случайно не знаете, почемуmockRestore
это привело кMath.Random
тому, что не был вызван?3. Причина, по которой он работает с вашей модификацией, заключается
mockReturnValueOnce(0)
в том, что он восстановит макет после первого вызоваMath.random
. ВосстановлениеrandomMock
просто означает, что любые последующие вызовыMath.random()
не будут издеваться. ноrandomMock
он больше не используется, поскольку он потерял привязку кMath.random
4. Вы можете проверить, что ответ работает так, как задумано, добавив a
console.log(getRandomId(42)
в оба теста (после редактирования в первом тесте и после восстановления во втором тесте). Запуск теста всегда выводит 1 в первом тесте и случайное целое число (в заданных границах) во втором случае.5. Это полезно знать! Я не понимал, что
randomMock
это потеряет свою привязку кMath.Random
aftermockRestore
is вызывается. Я также смог проверить это поведение при запуске своих тестов. Спасибо за объяснение.