47
votes

Comment tester des composants à l'aide de nouveaux hooks de routeur React?

Jusqu'à présent, dans les tests unitaires, les paramètres de correspondance du routeur de réaction étaient récupérés comme accessoires du composant. Donc, tester un composant en tenant compte d'une correspondance spécifique, avec des paramètres d'URL spécifiques, était facile: nous devions simplement préciser les accessoires de correspondance du routeur comme nous le souhaitons lors du rendu du composant en test (j'utilise la bibliothèque d'enzymes à cet effet).

J'apprécie vraiment les nouveaux hooks pour récupérer des trucs de routage, mais je n'ai pas trouvé d'exemples sur la façon de simuler une correspondance de routeur de réaction dans les tests unitaires, avec de nouveaux hooks de routeur de réaction?


0 commentaires

6 Réponses :


51
votes

J'ai fini par résoudre ce problème en me moquant des crochets dans mes tests en utilisant jest.mock:

// TeamPage.test.js
jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'), // use actual for all non-hook parts
  useParams: () => ({
    companyId: 'company-id1',
    teamId: 'team-id1',
  }),
  useRouteMatch: () => ({ url: '/company/company-id1/team/team-id1' }),
}));

J'utilise jest.requireActual pour utiliser les parties réelles de jest.requireActual -router-dom pour tout sauf les hooks qui m'intéressent de se moquer.


3 commentaires

Fonctionne comme un charme, et ce modèle sera utile dans de nombreux cas dans mon projet pour se moquer des points précis des modules externes sans tout casser :)


Je n'ai jamais jest.requireActual parler de jest.requireActual cela aide grandement!


et si je dois passer un identifiant de société différent dans le même fichier de test



41
votes

J'ai regardé les tests pour les hooks dans le repo MemoryRouter react-router et il semble que vous deviez envelopper votre composant dans un MemoryRouter et une Route . J'ai fini par faire quelque chose comme ça pour faire fonctionner mes tests:

import {Route, MemoryRouter} from 'react-router-dom';

...

const renderWithRouter = ({children}) => (
  render(
    <MemoryRouter initialEntries={['blogs/1']}>
      <Route path='blogs/:blogId'>
        {children}
      </Route>
    </MemoryRouter>
  )
)

J'espère que ça t'as aidé!


2 commentaires

Le problème est de se moquer des nouveaux hooks react-router-dom . Emballer votre composant dans un MemoryRouter est certainement ce que vous voulez faire pour tout composant testé qui se trouve dans un routeur. Il existe de nombreux modèles pour créer un wrapper réutilisable tel que testing-library.com/docs/example-react-router


Cette réponse doit être acceptée, moins intrusive, plus correcte



2
votes

J'essaie de savoir si la fonction push dans useHistory est appelée en faisant cela mais je ne peux pas obtenir les appels de fonction simulés ...

const mockHistoryPush = jest.fn();

jest.mock('react-router-dom', () => ({
    ...jest.requireActual('react-router-dom'),
    useHistory: () => ({
      push: mockHistoryPush,
    }),
  }));

fireEvent.click(getByRole('button'));
expect(mockHistoryPush).toHaveBeenCalledWith('/help');

Il dit que mockHistoryPush n'est pas appelé lorsque le bouton a onClick={() => history.push('/help')}


5 commentaires

jest se moque de hisser le module mockHistoryPush avant toute autre chose, ainsi votre mockHistoryPush ne sera pas vu au moment de l'exécution. Au lieu de cela, dans votre test, faites quelque chose comme import * as ReactRouterDom from 'react-router-dom'; jest.spyOn(ReactRouterDom, 'useHistory').returnValue({ push: mockHistoryPush, })


@JensBodal Je viens d'essayer et j'ai obtenu un "TypeError: Impossible de définir la propriété useHistory de [objet objet] qui n'a qu'un getter", se mettra à jour si je trouve une solution


Des nouvelles à ce sujet @JasonRogers? : '(


J'ai le même problème actuellement. Semble impossible de se moquer / tester cette situation.


L'histoire moqueuse.push est expliquée ici: stackoverflow.com/questions/58524183/...



21
votes

Dans votre composant, utilisez des crochets comme ci-dessous

import routeData from 'react-router';

const mockLocation = {
  pathname: '/welcome',
  hash: '',
  search: '',
  state: ''
}
beforeEach(() => {
  jest.spyOn(routeData, 'useLocation').mockReturnValue(mockLocation)
});

Dans votre test, espionnez l'objet reactRouter comme ci-dessous

import {useLocation} from 'react-router';

const location = useLocation()


3 commentaires

gentil, l'aide ci-dessus en utilisant spyOn Merci @suchin


Merci! Ça marche! comment avez-vous routeData ? Je ne le trouve pas dans la documentation de react-router.


Merci une petite correction de syntaxe: beforeEach(() => { jest.spyOn(routeData, 'useLocation').mockReturnValue(mockLocation) });



-2
votes

Si vous utilisez l' enzyme bibliothèque, je trouve un moyen de résoudre beaucoup moins bavard le problème ( en utilisant cette section du react-router-dom docs ):

import React from 'react'
import { shallow } from 'enzyme'
import { MemoryRouter } from 'react-router-dom'
import Navbar from './Navbar'

it('renders Navbar component', () => {
  expect(
    shallow(
      <MemoryRouter>
        <Navbar />
      </MemoryRouter>
    )
  ).toMatchSnapshot()
})


0 commentaires

3
votes

Si vous utilisez la react-testing-library pour les tests, vous pouvez faire fonctionner cette simulation.

jest.mock('react-router-dom', () => ({
    ...jest.requireActual('react-router-dom'),
    useLocation: () => ({ state: { email: 'school@edu.ng' } }),
}));

export const withReduxNRouter = (
    ui,
    { store = createStore(rootReducer, {}) } = {},
    {
    route = '/',
    history = createMemoryHistory({ initialEntries: [ route ] }),
    } = {}
) => {
    return {
    ...render(
        <Provider store={store}>
        <Router history={history}>{ui}</Router>
        </Provider>
    ),
    history,
    store,
    };
};

Vous auriez dû vous moquer de react-router-dom avant qu'il ne soit utilisé pour rendre votre composant. J'explore des moyens de rendre cela réutilisable


4 commentaires

Je testais une application ionique de base qui utilisait le hook useLocation. Cela a parfaitement fonctionné. Merci.


si vous avez utilisé CRA pour créer votre projet, vous pouvez simplement mettre le bloc jest.mock dans setupTests.js (ts)


Bonjour @chidimo, avez-vous trouvé un moyen de rendre cela réutilisable?


Je pense que je l'ai fait. J'ai fait un post que vous pouvez trouver ici smashingmagazine.com/2020/07/react-apps-testing-library