Skip to content

docs: replace "axiosMock" with "msw" in React Testing Library example #483

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 4, 2020
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 30 additions & 18 deletions docs/react-testing-library/example-intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,29 @@ See the following sections for a detailed breakdown of the test
```jsx
// __tests__/fetch.test.js
import React from 'react'
import { rest } from 'msw'
import { setupServer } from 'msw/node'
import { render, fireEvent, waitFor, screen } from '@testing-library/react'
import '@testing-library/jest-dom/extend-expect'
import axiosMock from 'axios'
import Fetch from '../fetch'

jest.mock('axios')
const server = setupServer(
rest.get('/greeting', (req, res, ctx) => {
return res(ctx.json({ greeting: 'hello there' }))
})
)

beforeAll(() => server.listen())
afterAll(() => server.close())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add an afterEach(() => server.resetHandlers()) here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a safe option—yes, but unless you declare runtime handlers via server.use() there’s nothing to reset. I’d not suggest calling resetHandlers by default. Developers that need it will find it :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a fair point. Now that you mention it, I think the server.use call should be inside the test rather than outside it. That's what people should be doing most of the time.

With that in mind, resetHandlers should be included.

And I think it should be used by default 😉

Copy link
Contributor Author

@kettanaito kettanaito Jun 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May I please double check on this?

I see your point of using resetHandlers and server.use inside a test suite, but I think I'm confused about what runtime handler to add via server.use, as the example tests exactly one API communication to "GET /greeting".

const server = setupServer(
  // centralized place for request handlers,
  // generally encouraged.
  rest.get('/greeting', resolver)
)

beforeAll(...)
afterAll(...)

test('loads and displays greeting', () => {
  // I need `afterEach(() => server.resetHandlers())`
  // only if I use `server.use()` in any of my tests.
  server.use(/* however, what should I add here? */)
})

Am I missing something in this? 🤔 Do you suggest to move rest.get('/greeting') from setupServer to server.use inside a test suite?

I'm slightly concerned that showcasing both initial (setupServer) and runtime handlers (server.use) can be overwhelming for the reader, so I'd try to keep their usage to a minimum.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a fair point. There are two things to consider:

  1. Handle all of our application's typical requests so we don't have to think about them on a per-test basis
  2. Add a handler for a specific test (to test an error state)

With that in mind, I think we should adjust this example so we have two tests. One for the success case, and one for the error case. That way the first one can rely on the default handlers and one that can add a runtime handler for the error.

Because I see using afterEach(() => server.resetHandlers()) as an important default whenever someone uses server.use and we should probably demonstrate that so people don't add a bunch of complicated single-test-specific handlers to their setupServer call.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is perfect.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added the error handling test scenario to this example.

  1. Rewrote useState to useReducer in fetch.js to reflect the error and data relation.
  2. Added a new test suite, used server.use() with permanent override there.
  3. Added afterEach that calls server.resetHanders().

Please, could you double check if that fetch.js looks okay to you? Thanks.


test('loads and displays greeting', async () => {
const url = '/greeting'
render(<Fetch url={url} />)

axiosMock.get.mockResolvedValueOnce({
data: { greeting: 'hello there' },
})

fireEvent.click(screen.getByText('Load Greeting'))

await waitFor(() => screen.getByRole('heading'))

expect(axiosMock.get).toHaveBeenCalledTimes(1)
expect(axiosMock.get).toHaveBeenCalledWith(url)
expect(screen.getByRole('heading')).toHaveTextContent('hello there')
expect(screen.getByRole('button')).toHaveAttribute('disabled')
})
Expand All @@ -47,17 +49,32 @@ test('loads and displays greeting', async () => {
// import dependencies
import React from 'react'

// import API mocking utilities from Mock Service Worker
// (see https://github.com/mswjs/msw)
import { rest } from 'msw'
import { setupServer } from 'msw/node'

// import react-testing methods
import { render, fireEvent, waitFor, screen } from '@testing-library/react'

// add custom jest matchers from jest-dom
import '@testing-library/jest-dom/extend-expect'
import axiosMock from 'axios'
// the component to test
import Fetch from '../fetch'

// https://jestjs.io/docs/en/mock-functions#mocking-modules
jest.mock('axios')
// declare which API requests to mock
const server = setupServer(
// capture "GET /greeting" requests
rest.get('/greeting', (req, res, ctx) => {
// respond using a mocked JSON body
return res(ctx.json({ greeting: 'hello there' }))
})
)

// establish API mocking before all tests
// and clean up once the tests are finished
beforeAll(() => server.listen())
afterAll(() => server.close())
```

```jsx
Expand All @@ -70,7 +87,8 @@ test('loads and displays greeting', async () => {

### Arrange

The [`render`](./api#render) method renders a React element into the DOM and returns utility functions for testing the component.
The [`render`](./api#render) method renders a React element into the DOM and
returns utility functions for testing the component.

```jsx
const url = '/greeting'
Expand All @@ -83,10 +101,6 @@ The [`fireEvent`](dom-testing-library/api-events.md) method allows you to fire
events to simulate user actions.

```jsx
axiosMock.get.mockResolvedValueOnce({
data: { greeting: 'hello there' },
})

fireEvent.click(screen.getByText('Load Greeting'))

// Wait until the mocked `get` request promise resolves and
Expand Down Expand Up @@ -133,8 +147,6 @@ export default function Fetch({ url }) {
```

```jsx
expect(axiosMock.get).toHaveBeenCalledTimes(1)
expect(axiosMock.get).toHaveBeenCalledWith(url)
expect(screen.getByRole('heading')).toHaveTextContent('hello there')
expect(screen.getByRole('button')).toHaveAttribute('disabled')

Expand Down