Portfolio Notes About

Testing React Apps workshop notes

Notes to self while learning testing react apps from this workshop.

“The more your test resemble the way your software is used, the more confidence they can give you.”

When we think about how things are used, we need to consider who the users are:

  1. The end user that’s interacting with our code (clicking buttons, using the UI)
  2. The developer user that’s actually using our code (rendering it, calling our functions, etc.)

Avoid the test user! (as much as possible)

React Testing Library

By default we can manually append the items we render to the body - but it’s boilerplate that could live in an abstraction. DOM Testing Library gives us numerous abstractions that make interacting with the DOM easier and more user-like.

Avoid implementation details

The implementation of our abstractions do not matter to the users of our abstractions. And if we want to have confidence that it continues to work through refactors then neither should our tests.

const { container } = render(<Counter />)
container.firstChild // <-- that's the button

However, what if we changed it a bit:

function Counter() {
  const [count, setCount] = React.useState(0)
  const increment = () => setCount(c => c + 1)
  return (
    <span>
      <button onClick={increment}>{count}</button>
    </span>
  )
}

Our tests would break!

React Testing Library gives us better way for API querying. https://testing-library.com/docs/dom-testing-library/api-queries#screen

Also gives us more user-like interactions with our UI (with @testing-library/user-event)

Form testing

Forms are very common and usually represent an important part of applications (checkout, login, register, etc.) Once again Testing Library has excellent selectors for form testing

Jest has built-in “mock” function APIs. Has useful utilities attached to it. E.g. To see how many times the function has been called, and with what arguments. https://jestjs.io/docs/en/mock-function-api

Generating test data

This also communicates that the generated data isn’t super specific that we have to type a certain way. E.g. “brucewillis” can really be generateRandomUserName()

Mocking HTTP requests

testing that our frontend code interacts with the backend is important. This also gives us maximum confidence - but the setup work required is non-trivial. However it is important and we will test integration with E2E tests using a tool like Cypress

For our integration and unit component tests, we’re going to trade-off some confidence for convenience and we’ll make up for that with E2E tests.

We’re using a tool called MSW, which is a request interceptor. It simplifies things a lot because we don’t have to worry about finding open ports, that we’re making requests to the right port, etc. Also allows us to mock requests made to other domains, can be more reliable, works offline, doesn’t require a lot of environment setup,

Snapshot Testing

Using one-off server handlers

When we want to test specific situations with the server, like the server misbehaving, we can use one-off server handlers! Collocate it, no need for it to muddle with the rest of the app.

Testing with context and a custom render method

We have to wrap every component that uses a context again and again.

Testing Library’s render function has a second parameter that accepts a wrapper.

The return value lets us rerender our component as well

function Wrapper({ children }) {
  return <ContextProvider>{children}</ContextProvider>
}

const { rerender } = render(<ComponentToTest />, { wrapper: Wrapper })

rerender(<ComponentToTest newProp={true} />)

We can even configure our render globally :point_down:

https://testing-library.com/docs/react-testing-library/setup/

Testing custom hooks

Often, the easiest and most straightforward way to test a custom hook is to create a component that uses it and then test that component instead.

Sometimes it’s hard to write a test component without making a pretty complicated “TestComponent.” For those situations, you can try something like this:

let result
function TestComponent(props) {
  result = useCustomHook(props)
  return null
}

// interact with and assert on results here

Another option is to use react-hooks-testing-library

https://github.com/testing-library/react-hooks-testing-library