chevron-left chevron-right

Piotr Nalepa Frontend, architektura i AI

Testing location change in useEffect in React

Ta wersja
blog.piotrnalepa.pl/en/2022/02/18/testing-location-change-in-useeffect-in-react
Opublikowano
Autor
, Staff Software Engineer
Kategoria
JavaScript
Uwagi
Napisz do mnie · Napisz na X

Ten wpis ma 4 lata. Od tego czasu przeglądarki i narzędzia mogły się zmienić, więc zanim użyjesz kodu, sprawdź go z aktualną dokumentacją.

Once I created a React component that was updating the app URL in useEffect based on some conditions. The logic was that whenever a user gets to the meetings list and none is selected yet, then by default a first item should be marked as selected. This information should be visible both in the UI (some special styles are applied then) and in the browser address bar (the id of a meeting should be a part of URL).

Sekcja 1Solution

In my app I’m using react-router-dom and the useHistory hook. Looks at the code:

Przykład 1JSX
import { useHistory, useParams } from 'react-router-dom`;

export const MyComponent = ({ meetings }) => {
    const hsitory = useHistory();
    const { meetingId } = useParams();

    useEffect(() => {
        if (!meetingId) {
            history.push('meetings/' + meetings[0].id /* 1234 */);
        } 
    }, [meetingId, meetings]);

    return <div>MyComponent: {meetingId}</div>; 
}

In my tests I tried to do something like this:

Przykład 2JSX
it('adds the first meeting to URL', () => {
    renderComponent();

    expect(window.location.pathname).toBe('meetings/1234')
});

Obviously, the test was failing. Because this URL looks differently on first render. It looks like that: /meetings

I tried to use waitFor like here:

Przykład 3JSX
await waitFor(() => {
    expect(window.location.pathname).toBe('meetings/1234')
});

But it was giving false positives - it was not testing it properly.

Ultimately, I found a better solution, which is spying on history methods. Check the code below:

Przykład 4JSX
import { createMemoryHistory } from 'history';
import { Router } from 'react-router-dom';

let history;

const renderComponent = () => {
    history = createMemoryHistory();

    render(<Router history={history}><MyComponent /></Router>);
}

it('tests location changes', () => {
    renderComponent();

    const pushSpy = jest.spyOn(history, 'push');

    expect(pushSpy).toHaveBeenLastCalledWith(`meetings/1234`);
    expect(pushSpy).toHaveBeenCalledTimes(1);
});

This way I was able to test the URL changes in a satisfying way.

Sekcja 2Summary

I hope you find this post useful. It’s a kind of Things-I-Learnt (TIL) post. If you have any questions, do not hesitate to ask. Happy coding!

Udostępnij
Uwagi

O autorze

Staff Software Engineer

Piszę na tym blogu od 2009 roku, a strony i aplikacje webowe tworzę zawodowo od 14 lat. Obecnie w Bishop Fox prowadzę frontend platformy Cosmos, od samego początku projektu. Wcześniej pracowałem m.in. w OLX, KUDO, Auto1 i eZ Systems. Piszę tu o tym, czego nauczyłem się w praktyce: o JavaScripcie, TypeScripcie, Reakcie, CSS-ie i architekturze frontendu. Występowałem też jako prelegent na konferencjach w Polsce i Europie. Masz pytanie albo pomysł na wpis? Daj znać!