Estoy escribiendo pruebas (con Jest y React Testing Library) para un componente React de formulario. Tengo un método que se ejecuta en el formulario de envío:
const onSubmit = (data) => { // ... setIsPopupActive(true); // ... }; y useEffect que se ejecuta después del cambio isPopupActive , también al enviar:
useEffect(() => { if (isPopupActive) { setTimeout(() => { setIsPopupActive(false); }, 3000); } }, [isPopupActive]);En la prueba, quiero verificar si la ventana emergente desaparece después de 3 segundos. Así que aquí está mi prueba:
it('Closes popup after 3 seconds', async () => { const nameInput = screen.getByPlaceholderText('Imię'); const emailInput = screen.getByPlaceholderText('Email'); const messageInput = screen.getByPlaceholderText('Wiadomość'); const submitButton = screen.getByText('Wyślij'); jest.useFakeTimers(); fireEvent.change(nameInput, { target: { value: 'Test name' } }); fireEvent.change(emailInput, { target: { value: 'test@test.com' } }); fireEvent.change(messageInput, { target: { value: 'Test message' } }); fireEvent.click(submitButton); const popup = await waitFor(() => screen.getByText(/Wiadomość została wysłana/) ); await waitFor(() => { expect(popup).not.toBeInTheDocument(); // this passes expect(setTimeout).toHaveBeenCalledTimes(1); expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 3000); }); });Sin embargo, estoy recibiendo el error:
expect(received).toHaveBeenCalledTimes(expected) Matcher error: received value must be a mock or spy function Received has type: function Received has value: [Function setTimeout]¿Qué estoy haciendo mal?
En su caso, setTimeout no es un simulacro o un espía, sino una función real. Para convertirlo en un espía, use const timeoutSpy = jest.spyOn(window, 'setTimeout') . Y use timeoutSpy en la afirmación.
También puede probar no el hecho de llamar a la función setTimeout , sino afirmar que setIsPopupActive se llamó una vez y con false . Para esto, es posible que deba hacer jest.runOnlyPendingTimers() o jest.runAllTimers()
Jest 27 tiene cambios importantes para fakeTimers. Parece que los colaboradores de Jest no actualizan la documentación a tiempo. Este comentario sobre problemas de Github lo confirma. Además, aquí relacionado PR.
Bueno, puedes resolver tu problema de dos maneras.
module.exports = { // many of lines omited timers: 'legacy' }; jest.useFakeTimers('legacy'); describe('My awesome logic', () => { // blah blah blah });Es preferible usar una nueva sintaxis basada en @sinonjs/fake-timers . Pero no puedo encontrar un ejemplo de trabajo para Jest , así que actualizaré esta respuesta lo antes posible.
El siguiente enfoque funcionó
beforeEach(() => { jest.spyOn(global, 'setTimeout'); }); afterEach(() => { global.setTimeout.mockRestore(); }); it('Test if SetTimeout is been called', { global.setTimeout.mockImplementation((callback) => callback()); expect(global.setTimeout).toBeCalledWith(expect.any(Function), 7500); })