es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Probando un gancho personalizado UseInterval con jest.

Mi gancho es:

function useInterval() {
  const ref = useRef(null);
  function set(callback, delay) {
    ref.current = setInterval(callback, delay)
  }
  function clear() {
    if (ref.current) {
      clearInterval(ref.current)
      ref.current = null
    }
  }
  return { set, clear }
}

Mi prueba es:

it("set: Esto debería ser llamado 10 veces", () => {
    var callback = jest.fn();
    jest.useFakeTimers()
    const { result } = renderHook(() => hooks.useInterval())
    act(() => {
        result.current.set(() => { callback }, 100);
        jest.advanceTimersByTime(1000);
    })
    expect(callback).toHaveBeenCalledTimes(10);
    jest.useRealTimers()
})

renderHook() y act() provienen de "@testing-library/react-hooks": "^7.0.2"

El resultado que sigo obteniendo es 0 en mi llamada expect(). No puedo encontrar la razón.

Si solo uso setInterval(), expect() obtiene el valor correcto.

it("setInterval", () => {
    var callback = jest.fn();
    jest.useFakeTimers()
    setInterval(callback, 100)
    jest.advanceTimersByTime(1000);
    expect(callback).toHaveBeenCalledTimes(10);
    jest.useRealTimers()
})

He intentado reordenar las líneas de todas las formas lógicas posibles que se me ocurren.

He notado que obtengo el mismo resultado con o sin act(), extrañamente.

Agregar timers: "fake" o cualquiera de sus variaciones (modern/legacy) a jest.config.ts no parece tener ningún efecto.

Obviamente, testing-library/react-hooks de alguna manera está ocultando setInterval() de jest.useFakeTimers(), pero no entiendo cómo y, por lo tanto, no puedo obtener el resultado que espero.

Parte de mí piensa que mi gancho no está siendo afectado por jest.useFakeTimers() porque los temporizadores falsos no se están reemplazando globalmente, pero no sé cómo hacer esto.

Además, estoy usando Typescript. Aunque no creo que eso haga una diferencia.

Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    Solución de problemas de un caso de prueba fallido para un gancho React personalizado

    El problema

    La llamada expect() en el caso de prueba proporcionado devuelve 0, lo que indica que la función callback pasada al método set() del gancho personalizado useInterval() no se ha llamado. El mismo caso de prueba con setInterval() en lugar del gancho personalizado funciona como se esperaba.

    Posibles causas

    • jest.useFakeTimers() podría no estar simulando correctamente setInterval() y clearInterval() en el contexto del gancho personalizado.
    • act() podría no ser necesario o podría no usarse correctamente en este caso de prueba.
    • La interacción entre el gancho personalizado y la biblioteca de pruebas puede no estar configurada correctamente o puede haber un error en una versión de estas bibliotecas.

    Posibles soluciones

    1. Para solucionar el problema de jest.useFakeTimers(), intente sustituir la función console.log() por jest.advanceTimersByTime() en el caso de prueba y verifique si la salida muestra el número esperado de registros.
      it('set: Esto debería ser llamado 10 veces', () => {
       var callback = jest.fn();
       jest.useFakeTimers()
       const { result } = renderHook(() => useInterval())
       result.current.set(() => { callback() }, 100)
      
       [...new Array(10)].forEach((_, index) => {
         jest.advanceTimersByTime(100);
         console.log('Tick', index + 1);
       });     
       jest.useRealTimers();
       expect(callback).toHaveBeenCalledTimes(10);
      });
      

      Si la salida en la consola es la esperada pero la función callback aún no se está llamando, el problema podría estar relacionado con la interacción entre la biblioteca de pruebas y el gancho personalizado, como se explica a continuación.

    2. Para solucionar el problema de act(), intente eliminarlo del caso de prueba y verifique si la aserción expect() se cumple.

      it('set: Esto debería ser llamado 10 veces', () => {
        var callback = jest.fn();
        jest.useFakeTimers()
        const { result } = renderHook(() => useInterval())
        result.current.set(() => { callback() }, 100)
        jest.advanceTimersByTime(1000);
        jest.useRealTimers();
        expect(callback).toHaveBeenCalledTimes(10);
      });
      

      Si la aserción expect() se cumple sin act(), puede que no sea necesario en este caso de prueba. Si falla, intente envolver todo el caso de prueba dentro de act().

      it('set: Esto debería ser llamado 10 veces', () => {
        var callback = jest.fn();
        jest.useFakeTimers()
        act(() => {
          const { result } = renderHook(() => useInterval())
          result.current.set(() => { callback() }, 100)
        });
        jest.advanceTimersByTime(1000);
        jest.useRealTimers();
        expect(callback).toHaveBeenCalledTimes(10);
      });
      

    3. Para verificar si el gancho personalizado y la biblioteca de pruebas están configurados correctamente, asegúrese de que:
      • El gancho se importe correctamente y tenga la firma adecuada.
      • La biblioteca de pruebas se importe correctamente e incluya renderHook() y act().
      • El gancho personalizado se invoque con renderHook() y se extraiga correctamente el resultado.
      • Los métodos set() y clear() del gancho personalizado se llamen con los argumentos correctos y se comporten como se esperaba.
      // importe los módulos necesarios
      import {renderHook, act} from '@testing-library/react-hooks';
      import useInterval from './useInterval'; // o la ruta a su gancho personalizado
      
      // defina el caso de prueba
      it('set: Debería funcionar', () => {
       const callback = jest.fn();
       jest.useFakeTimers();
       const { result } = renderHook(() => useInterval());
       expect(result.current).toMatchObject({ set: expect.any(Function), clear: expect.any(Function) });
       act(() => {
         result.current.set(callback, 100);
       });
       expect(setInterval).toHaveBeenLastCalledWith(expect.any(Function), 100); // verificación simulada
       jest.advanceTimersByTime(1000);
       expect(callback).toHaveBeenCalledTimes(10);
       act(() => {
         result.current.clear();
       });
       expect(clearInterval).toHaveBeenCalledTimes(1); // verificación simulada
       jest.useRealTimers();
      });
      

    Si ninguna de las soluciones anteriores funciona, intente actualizar la versión de las bibliotecas de pruebas utilizadas (@testing-library/react-hooks y/o jest) a la última versión estable. Si el problema persiste, consulte la documentación y/o abra un problema en el repositorio respectivo.

Comments are closed.