В этом случае я на 99% уверен, что проблема в том, что вы издеваетесь слишком поздно.
const auth = new AuthUtils();
- это встроенный код в файле модуля. Это означает, что он выполняется сразу после импорта файла.
Ваш тестовый файл запускает код в следующем порядке:
import { resumeSession } from '../../src/actions/AuthActions';
// this does:
// import AuthUtils from '../utils/AuthUtils';
// const auth = new AuthUtils();
import { setUser } from '../../src/actions/UserActions';
jest.mock('../../src/utils/UserActions');
const mockResume = jest.fn(() => Promise.resolve({ user: { things } }));
jest.mock('../../src/utils/AuthUtils', () => {
return jest.fn().mockImplementation(() => {
return { resume: mockResume };
});
});
// too late, since the code from the *actual* AuthUtils has already been executed
Этот будет работать нормально, если auth
была локальной переменной в вашей функции resumeSession
, например:
export const resumeSession = () => async (dispatch, getState) => {
const auth = new AuthUtils();
try {
const resumeResult = await auth.resume(); // wait for result
dispatch(setUser(resumeResult)); //dispatch setUser with result
} catch() {
}
};
Потому что тогда макет настраивается до того, как какой-либо код попытается использовать AuthUtils
. Но я предполагаю, что вы по какой-то причине создаете auth
вне функции.
Если перемещение экземпляра auth
внутрь вашей функции не вариант, одно из возможных решений - вместо этого перенести макет и настроить AuthUtils
и его функция resume
для до вашего импорта из AuthActions
:
const mockResume = jest.fn(() => Promise.resolve({ user: { things } }));
jest.mock('../../src/utils/AuthUtils', () => {
return jest.fn().mockImplementation(() => {
return { resume: mockResume };
});
});
import { resumeSession } from '../../src/actions/AuthActions';
import { setUser } from '../../src/actions/UserActions';
jest.mock('../../src/utils/UserActions');
Если это не сработает (или если вы предпочитаете не иметь никакого кода раньше ваш импорт), другой вариант - экспортировать переменную auth
, чтобы вы могли шпионить за фактическим экземпляром и высмеивать его resume
функцию:
import { auth, resumeSession } from '../../src/actions/AuthActions';
const mockResume = jest.fn(() => Promise.resolve({ user: { things } }));
jest.spyOn(auth, "resume").mockImplementation(mockResume);
Эта может иметь сторону эффект сохранения вашей проверенной реализации для других тестов после того, как этот будет сделан, что вы, вероятно, не хотите. Вы можете использовать методы жизненного цикла Jest, чтобы избежать этого и восстановить первоначальную реализацию resume
, когда ваши тесты будут завершены:
const mockResume = jest.fn(() => Promise.resolve({ user: { things } }));
const resumeSpy = jest.spyOn(auth, "resume");
resumeSpy.mockImplementation(mockResume);
describe('resumeSession', () => {
afterAll(() => {
resumeSpy.mockRestore();
});
it('dispatches complete', async () => {
const mockDispatch = jest.fn();
const mockGetState = jest.fn();
await resumeSession()(mockDispatch, mockGetState);
expect(setUser).toHaveBeenCalledWith({ user: { things } });
});
});
Несвязанный sidenote: Функции Jest Mock (и шпионы) имеют удобную функцию для фиктивные результаты Promise, поэтому вам не нужно иметь фиктивную реализацию, которая вручную вызывает Promise.resolve()
или Promise.reject()
. Я лично предпочитаю использовать собственные функции Jest:
const mockResume = jest.fn();
mockResume.mockResolvedValue({ user: { things } }));
Если вы используете шпионский подход, вы можете вообще отказаться от функции mockResume
:
const resumeSpy = jest.spyOn(auth, "resume");
resumeSpy.mockResolvedValue({ user: { things } }));
Это не связано с проблемой у вас сейчас есть, но я решил выбросить это туда.