У меня есть тест, который я пытаюсь запустить, и я хочу имитировать модуль с помощью jest.mock. Однако мне не удается заставить этот метод работать: в приведенном ниже примере всегда вызывается исходный метод serializeProduct. Я пробовал шпионить, я пробовал издеваться над модулем. Когда я вывожу из системы переменные, которые являются издевательствами/шпионами, они действительно являются фиктивными функциями, по крайней мере, так что издевательство происходит. Просто что-то заставляет его вызывать этот оригинальный метод вместо макета.
Product.serializer.ts
import { ProductDto } from './product.dto';
export function serializeProducts(value: any[]): ProductDto[] {
return value.map(serializeProduct);
}
export function serializeProduct(value: any) {
return value;
}
Product.serializer.spect.ts в той же папке, что и выше.
import { expect } from '@jest/globals';
import * as productsSerializer from './products.serializer';
describe('serializer', () => {
afterEach(() => {
jest.clearAllMocks();
jest.restoreAllMocks();
});
describe('serializeProducts method', () => {
it('should foo', () => {
const packages = [1,2,3,];
const spy = jest.spyOn(productsSerializer, 'serializeProduct').mockImplementation(() => undefined);
productsSerializer.serializeProducts(packages);
expect(spy).toHaveBeenCalledTimes(3);
});
});
});
Я также пробовал издеваться над модулем изначально вот так
jest.mock('./products.serializer', () => ({
...jest.requireActual('./products.serializer'),
serializeProduct: jest.fn()
});
import { expect } from '@jest/globals';
import * as productsSerializer from './products.serializer';
describe('serializer', () => {
afterEach(() => {
jest.clearAllMocks();
jest.restoreAllMocks();
});
describe('serializeProducts method', () => {
it('should foo', () => {
const packages = [1,2,3,];
(productsSerializer.serializeProduct as jest.Mock).mockImplementation(() => undefined);
productsSerializer.serializeProducts(packages);
expect(productsSerializer.serializeProduct).toHaveBeenCalledTimes(3);
});
});
});
Я также отдельно импортировал методы из приведенного выше примера вместо импорта * as, но безрезультатно.
У меня есть примеры того, как это работает в том же проекте, над которым я работаю, но в этом нет.
РЕДАКТИРОВАТЬ Принятое решение по предложенному другому вопросу нежизнеспособно, поскольку требует изменения кода приложения для исправления теста.
Другое решение — переместить экспорт, а также изменить код приложения. Я подтвердил, что если я удалю один из методов, он сработает, но на самом деле это тоже не должно быть жизнеспособным решением. Для кого-то это может сработать, но в контексте моего приложения это не имеет смысла.
Поэтому я хотел бы выяснить, почему это не работает. Если это сработает, то должно быть решение, не требующее изменения кода приложения.
РЕДАКТИРОВАТЬ 2 Я обнаружил, что преобразование методов в экспортированные константы и определение их как стрелочных функций решает проблему. Это означает, что это, вероятно, как-то связано с импортом jest/node.
@Brother58697, какое решение ты бы выбрал? Ответ нежизнеспособен. И следующий по количеству голосов ответ тоже нежизнеспособен. Экспорт не будет разбиваться на разные файлы. Согласно шутливым документам, это должно работать. Я мог бы показать вам сотни рабочих примеров в моем текущем проекте, особенно техники spyOn. Поэтому любопытно, что именно этот экземпляр не работает. Я предполагал, что это сработает, когда урезал его до такой степени после удаления всего кода, специфичного для приложения.
Вот интересная тема по этому вопросу. Но наименее навязчивое решение, которое я только что попробовал из ветки, — это использовать функции стрелок. Теперь это зависит от вашего трансформатора (поскольку вы найдете некоторые комментарии, которые не смогли заставить его работать). Тот, который у меня получился «из коробки», — это ts-jest. Так что используйте export const serialize = () => {}, настройте ts-jest и дайте мне знать, если это сработает (да, это только что сработало для меня), если да, я напишу ответ
Теперь я вижу в вашем редактировании, что вы пришли к тому же решению до того, как я написал свой комментарий. Прочитайте первые несколько комментариев в выпуске github, чтобы понять, почему это происходит, в противном случае я рад, что теперь это работает.
@Brother58697Brother58697, не стесняйтесь писать ответ, и я приму его. Спасибо, я, конечно, не смог найти эту статью сам, и я даже просмотрел их github, прежде чем опубликовать здесь.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


У вас возникли проблемы с издевательством serializeProduct, потому что serializeProducts вызывает его из того же модуля. Основная причина этой проблемы заключается в том, как передается код.
Давайте посмотрим на следующий пример:
export function foo() {};
export function bar() { foo() };
Ваш транспилятор, скорее всего, преобразует его во что-то вроде этого (упрощенно):
function foo() {};
function bar() { foo() };
exports.foo = foo;
exports.bar = bar;
Проблема здесь в том, что мы имитируем (переназначаем) exports.foo в наших тестах, в то время как bar вызывает оригинал foo.
Одно решение
Итак, единственное, что мы можем сделать, это вызвать экспортированное свойство foo, иначе говоря, exports.foo, чтобы, когда оно будет имитировано, вместо этого мы вызывали макет.
export function foo() {};
export function bar() { exports.foo() };
Это действительный код, и он будет правильно транспилирован, но он некрасив.
Лучшее решение для вас
Поскольку вы используете Typescript, велика вероятность, что вы используете ts-jest в качестве преобразователя. У ts-jest есть небольшая приятная особенность: он заменяет экспортированные переменные их аналогами-членами exports.
Таким образом, вместо вызова функции, которую мы определяем с помощью оператора функции, нам просто нужно вызвать экспортированную переменную, назначенную нужной нам функции, и эта переменная будет издевательской из-за полезных подстановок ts-jest. Мы можем сделать это разными способами:
function foo () {};
// All 3 of these are replaced by `exports.<name>` by `ts-jest` where they're called
export const foo1 = foo; // Reassign to a new variable
export const foo2 = function() {} // Anonymous function
export const foo3 = () => {} // Arrow function
Вот небольшая иллюстрация, которую я получил при отладке теста, который запускал локально. Давайте посмотрим исходный модуль и то, как его преобразовал ts-jest.
// Original
export function foo() {};
export const bar = function() {}
export function buzz() {
foo();
bar();
}
// Transformed
Object.defineProperty(exports, "__esModule", { value: true });
exports.bar = void 0;
exports.foo = foo;
exports.buzz = buzz;
function foo() {};
const bar = () => {};
exports.bar = bar;
function buzz() {
foo();
(0, exports.bar)();
}
Обратите внимание на то, как в buzz вызов функции bar был заменен ее аналогом exports. Теперь мы можем без проблем использовать spyOn.
Больше чтения: Проблема на GitHub, посвященная именно этой ситуации.
Поощрительное упоминание — внедрение зависимостей
В вашем конкретном случае serializeProducts по сути является оберткой вокруг Array.map, что делает его основным кандидатом для некоторого внедрения зависимостей. Вы можете дать ему второй параметр, передать его serializeProduct по умолчанию, а затем передать ему фиктивную функцию в своих тестах.
Это дает serializeProducts дополнительную степень гибкости, не слишком сильно меняя общий рабочий процесс, и значительно упрощает тестирование.
Этот вопрос похож на: Как имитировать функции в одном модуле с помощью Jest?. Если вы считаете, что это другое, отредактируйте вопрос, поясните, чем он отличается и/или как ответы на этот вопрос не помогают решить вашу проблему.