Skip to content

Consider showcasing "msw" package for API mocking #482

Closed
@kettanaito

Description

@kettanaito

What

I'd like to suggest to use Mock Service Worker in test examples that concern API mocking.

Why

  • MSW uses a declarative API to describe requests that should be mocked.
  • MSW captures all outgoing network communication on both client and server. It also runs in a fall-through mode, which means unrelated requests are left intact.
  • MSW utilizes Service Worker API on the client-side usage, making the mocks completely detached from the application. Unlike most of the other solutions, that means a request interception on the network level, not application level (i.e. making even the mocked requests/responses inspectable in the "Network" tab of your browser).
  • MSW is not opinionated over how you construct your mock definitions. You can use plain request-response contracts, or bring in ORM and integrate it closer to your actual server's logic. The beauty if that you don't have to do that.
  • MSW can be used on both client and server, being agnostic of frameworks and request-issuing libraries (use React, Vue, fetch or axios—you're covered).

Looks like @kentcdodds would support recommending msw as a part of Testing Library ❤️ .

What would it bring?

  • Discourage people to stub window.fetch for the sake of API mocking. Instead, they would describe how an imaginary server would respond.
  • Educate people on how Service Worker can be used for API mocking (the approach is relatively new and not explored).

Where

Here's the "Example" page that showcases the usage of axiosMock.get.mockResolvedValueOnce:

This example alone brings some of the following pain-points:

  • It assumes that developer is using axios, making it a test's dependency.
  • It stubs axios.get method to make it return what you need.
  • Asserts of axiosMock.get was called, while it's not a part of the functionality we are testing.

Compare the page above with how it may look like with MSW:

// __tests__/fetch.test.js
import React from 'react'
import { render, fireEvent, waitFor, screen } from '@testing-library/react'
import '@testing-library/jest-dom/extend-expect'
import { rest } from 'msw'
import { setupServer } from 'msw/node'
import Fetch from '../fetch'

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

beforeAll(() => server.listen())
afterAll(() => server.close())

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

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

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

  expect(screen.getByRole('heading')).toHaveTextContent('hello there')
  expect(screen.getByRole('button')).toHaveAttribute('disabled')
})

Notice how test suite itself now knows nothing about the mocking. Because it shouldn't. We also remove the assertion that axiosMock.get was called, because it is not related to what we are actually testing.

How

I would be glad to issue a pull request regarding these changes. Any guidance on that is also highly appreciated!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions