Slide 1

Slide 1 text

🛂 ADVANCED TESTING CONCEPTS Another CodingWithCallum™️ Session

Slide 2

Slide 2 text

WHAT ARE WE GOING TO TALK ABOUT? Async rendering in React Testing Library (RTL) wtf is act Ordering queries in RTL (a11y, eg. getByRole > getByDataId)

Slide 3

Slide 3 text

WHAT.ANZ.COM Abbrevs: Async = Asynchronous RTL = React Testing Library MSW = Mock Service Worker Abbrev = Abbreviation

Slide 4

Slide 4 text

ASYNC RENDERING IN REACT TESTING LIBRARY Wtf is this act warning and why do i see it all the time? When testing, code that causes React state updates should be w act(() => { /* fire events that update state */ }); /* assert on the output */

Slide 5

Slide 5 text

This ensures that you're testing the behavior the user would s

Slide 6

Slide 6 text

BUT WHY? This can happen, before, during or after (most likely) the test React is trying to warn us something happened when we expected nothing to happen at all

Slide 7

Slide 7 text

EXAMPLE 1: ASYNC UPDATES const ShowName = () => { const [person, setPerson] = useState(); const handleFetch = useCallback(async () => { const { data } = await fetchData(); // Returns { name: "Ca setPerson(data.person); // <- Asynchronous update }, []); return ( {person ? person.name : "Fetch"} ); }; it("should fetch persons name", () => { render(); fireEvent.click(screen.getByText("Fetch")); expect(screen.getByText("Callum")).toBeInTheDocument(); });

Slide 8

Slide 8 text

EXAMPLE 1: SEQUENCE assert 'Callum' exists fireEvent.click fetchData setPerson rerender 'Callum' rendered

Slide 9

Slide 9 text

EXAMPLE 1: SOLUTION it("should fetch persons name", async () => { render(); fireEvent.click(getByText("Fetch")); expect(screen.getByText("Fetch names")).toBeInTheDocument(); expect(await screen.findByText("Callum")).toBeInTheDocument( });

Slide 10

Slide 10 text

EXAMPLE 2: TIMERS const Toast = () => { const [isVisible, setIsVisible] = useState(true); useEffect(() => { setTimeout(() => { setIsVisible(false); }, 1000); // hide }, []); return isVisible &&
Toast!
; }; it("should display Toast for 1 second", () => { jest.useFakeTimers(); render(); jest.advanceTimersByTime(1000); expect(screen.queryByText("Toast!")).not.toBeInTheDocument() });

Slide 11

Slide 11 text

EXAMPLE 2: SEQUENCE assert 'Toast' not exists jest.advanceTimersByTime advances by 1 second rerender 'Toast!' removed

Slide 12

Slide 12 text

EXAMPLE 2: SOLUTION it("should display Toast for 1 second", () => { jest.useFakeTimers(); render(); act(() => { // Actually wrap it in an act 😅 jest.advanceTimersByTime(1000); }); expect(screen.queryByText("Toast!")).not.toBeInTheDocument() });

Slide 13

Slide 13 text

EXAMPLE 3: PREMATURE EXIT const ShowName = () => { const { loading, data } = await fetchData(); // Returns { na return loading && data?.name ? (
Loading...
) : (
{data.name}
); }; it("should display loading state", () => { const render(); expect(screen.getByText("Loading ...")).toBeInTheDocument(); });

Slide 14

Slide 14 text

EXAMPLE 3: SEQUENCE assert 'Loading...' exists render show 'Loading...' fetch returns Show 'data'

Slide 15

Slide 15 text

EXAMPLE 3: SOLUTION even better... it("should display loading state", () => { const render(); expect(getByText("Loading ...")).toBeInTheDocument(); await waitFor(() => { expect(queryByText("Loading ...")).not.toBeInTheDocument() }); }); it("should display loading state", () => { const render(); expect(getByText("Loading ...")).toBeInTheDocument(); await waitForElementToBeRemoved(() => queryByText("Loading . });

Slide 16

Slide 16 text

PHEW 😮‍💨 That was fun, questions?

Slide 17

Slide 17 text

ORDERING OF QUERIES IN RTL What should I use in what circumstance? How do I remember all these queries? Whats the difference? Water break 🚰

Slide 18

Slide 18 text

TYPES OF QUERIES At high level there's 3 types urns match but fails if none found ByText("Callum") returns match but null if none found ect(queryByText("Callum").not.toBeInTheDo turns promise that resolves with match found or errors on it findByText("Callum")

Slide 19

Slide 19 text

SINGLE VS MULTIPLE Single (by) getBy queryBy findBy Multiple (allBy) getAllBy queryAllBy findAllBy

Slide 20

Slide 20 text

Single Element getBy... Throw error Return element Throw error No queryBy... Return null Return element Throw error No findBy... Throw error Return element Throw error Yes Multiple Elements getAllBy... Throw Return Return No

Slide 21

Slide 21 text

Query Type 0 Match 1 Match >1 Match Async/Awa error array array queryAllBy... Return [] Return array Return array No findAllBy... Throw error Return array Return array Yes

Slide 22

Slide 22 text

ORDER OF QUERYING 1. Focus on accessibility (reflect the experience of visual/assistive users) 2. Semantic Queries (HTML5 and ARIA compliant selectors) 3. Test IDs (last resort, user cannot see or hear these)

Slide 23

Slide 23 text

getByRole - can be used to query every element that is exposed in the name option can filter the returned elements by their Check the eg. getByRole('button', {name: /submit/i}) getByLabelText Navigating form fields getByText Outside of forms, text content is the main way users find elements accessibility tree accessible name list of accessible roles

Slide 24

Slide 24 text

getByDisplayValue Navigate a page with filled in values

Slide 25

Slide 25 text

2. SEMANTIC QUERIES getByAltText - If your element is one which supports alt text (img, area, input, and any custom element), then you can use this to find that element. getByTitle - The title attribute is not consistently read by screenreaders, and is not visible by default for sighted users

Slide 26

Slide 26 text

3. TEST IDS getByTestId: The user cannot see (or hear) these, so this is only recommended for cases where you can't match by role or text or it doesn't make sense (e.g. the text is dynamic).

Slide 27

Slide 27 text

FINALLY use await findByRole() to avoid rendering problems and be as accessible as possible check our console, act errors are painful and cause

Slide 28

Slide 28 text

FURTHER READING Fix the "not wrapped in act(...)" warning React testing library - queries The accessibility tree Accessible names List of accessible roles

Slide 29

Slide 29 text

No content