reactjs - Testa anpassad hook med SetTimeout och useEffect med Jest


Jag försöker testa en relativt enkel egen hook som använder useEffect och setTimeout. Men mitt test misslyckas, och jag kan inte lista ut vad som är fel.

Här är själva hooken (useTokenExpirationCheck.ts)

import { useEffect } from 'react';
import { logout } from '../features/profile/profileSlice';
import { useAppDispatch } from '../store/hooks';

export default function useTokenExpirationCheck(exp: number): void {
  const dispatch = useAppDispatch();
  useEffect(() => {
    if (exp) {
      const timeToLogout = exp * 1000 - Date.now();
      setTimeout(() => {
        dispatch(logout());
      }, timeToLogout);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [exp]);
}

och min testfil:

import { act, renderHook } from '@testing-library/react-hooks';
import useTokenExpirationCheck from './useTokenExpirationCheck';

jest.mock('../features/profile/profileSlice');
const logout = jest.fn();
const exp = Date.now() + 6000;

describe('Expiration token', () => {
  test('should logout user', async () => {
    jest.useFakeTimers();
    act(() => {
      renderHook(() => {
        useTokenExpirationCheck(exp);
      });
    });
    expect(logout).toHaveBeenCalledTimes(0);
    jest.advanceTimersByTime(60000);
    expect(logout).toHaveBeenCalledTimes(1);
  });
});

Det jag vet är att exp-variabeln på något sätt inte skickas till useTokenExpirationCheck-funktionen (console.log visade mig 0 inuti funktionen när den kördes). Så i princip kommer jag inte ens till useEffect själv... några idéer om vad som kan gå fel?

Svar #1

Här är min teststrategi:

  1. Jag kommer att använda redux-mock-store för att skapa en mock-store

Den simulerade butiken kommer att skapa en lista över skickade åtgärder som fungerar som en åtgärdslogg för tester.

Så att jag kan hämta och bekräfta de skickade åtgärderna med store.getActions()-metoden.

  1. Jag kommer att mocka Date.now()-metoden med ett mockat returvärde så att testerna inte längre beror på systemdatum. Systemdatum kan vara olika för olika tidszoner och olika CI/CD-servrar.

  2. Använd Props för att uppdatera indata (beroenden för useEffect) och återrendera hooken så att vi kan testa fallet: Om exp ändras.

useTokenExpirationCheck.ts:

import { useEffect } from 'react';
import { useDispatch } from 'react-redux';

const logout = () => ({ type: 'LOGOUT' });

export default function useTokenExpirationCheck(exp: number): void {
  const dispatch = useDispatch();
  useEffect(() => {
    if (exp) {
      console.log('exp: ', exp);
      const timeToLogout = exp * 1000 - Date.now();
      setTimeout(() => {
        dispatch(logout());
      }, timeToLogout);
    }
  }, [exp]);
}

useTokenExpirationCheck.test.tsx:

import { renderHook } from '@testing-library/react-hooks';
import React from 'react';
import { Provider } from 'react-redux';
import createMockStore from 'redux-mock-store';
import useTokenExpirationCheck from './useTokenExpirationCheck';

describe('useTokenExpirationCheck', () => {
  test('should dispatch logout action after delay', async () => {
    let exp = 6000;
    jest.spyOn(Date, 'now').mockReturnValue(5900 * 1000);
    const mockStore = createMockStore([]);
    const store = mockStore({});
    jest.useFakeTimers();
    const { rerender } = renderHook(() => useTokenExpirationCheck(exp), {
      wrapper: ({ children }) => <Provider store={store}>{children}</Provider>,
    });

    jest.advanceTimersByTime(100 * 1000);
    expect(store.getActions()).toEqual([{ type: 'LOGOUT' }]);

    exp = 6100;
    rerender();
    jest.advanceTimersByTime(200 * 1000);
    expect(store.getActions()).toEqual([{ type: 'LOGOUT' }, { type: 'LOGOUT' }]);
  });
});

testresultat:

 PASS  examples/69967414/useTokenExpirationCheck.test.tsx (8.329 s)
  useTokenExpirationCheck
    ✓ should pass (39 ms)

  console.log
    exp:  6000

      at examples/69967414/useTokenExpirationCheck.ts:10:15

  console.log
    exp:  6100

      at examples/69967414/useTokenExpirationCheck.ts:10:15

------------------------|---------|----------|---------|---------|-------------------
File                    | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
------------------------|---------|----------|---------|---------|-------------------
All files               |     100 |       50 |     100 |     100 |                   
 ...nExpirationCheck.ts |     100 |       50 |     100 |     100 | 9                 
------------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        8.885 s, estimated 10 s
Ran all test suites related to changed files.

Relaterade artiklar