Error responses

I start with restructuring our project a little bit. In order for me to use runtime handlers, I need to have access to the same server object created in vitest.setup.ts. However, importing from the setup file is not recommended.
πŸ¦‰ Although Vitest will cache imports and not evaluate the global hooks twice if you import from the setup file, it caches imports per test file. This works nice in the isolated mode, which Vitest uses by default, but may cause problems if you switch to parallel test runs.
/
  /mocks
    handlers.ts # Request handlers for happy paths.
    node.ts # Node.js integration point.
  get-auth-token.test.ts # import { server } from '...'
  vitest.setup.ts # import { server } from '...'
With these changes, I can reuse the same server object from ./mocks/node.ts in both vitest.setup.ts and in the test suite as well.
import { server } from './mocks/node.js'

beforeAll(() => {
  server.listen({
    onUnhandledRequest: 'error',
  })
})

afterEach(() => {
  server.resetHandlers()
})

afterAll(() => {
  server.close()
})
import { http } from 'msw'
import { server } from './mocks/node.js'
import { getAuthToken } from './get-auth-token.js'
At last, I can prepend request handlers using the server.use() API to model network scenarios on a per-test basis! The workflow for overriding the network will be the same no matter where I'm going to use it:
  1. Go to the test case that needs a different network behavior;
  2. Call server.use() with a request handler for the same request but describe a different mocked response;
  3. Execute the tested code (the code that does the request).
Here's how the invalid credentials scenario will look like with the runtuime handler in place:
test('throws an error if the user credentials are invalid', async () => {
  server.use(
    http.post('https://api.example.com/auth', () => {
      return new Response(null, { status: 401 })
    }),
  )

  await expect(() =>
    getAuthToken({
      email: 'kody@epicweb.dev',
      password: 'supersecret123',
    }),
  ).rejects.toThrow('Authentication failed: invalid credentials')
})
server.use() prepends request handlers so they take higher priority. The prepended handlers will persist until server.resetHandlers() function is called, which in our case is called in the afterEach() hook in vitest.setup.ts.
In a similar fashion, I will create a runtime request handler to emulate a generic 500 server error response in another test case:
test('throws an error if the server responds with an error', async () => {
  server.use(
    http.post('https://api.example.com/auth', () => {
      return new Response(null, { status: 500 })
    }),
  )
Lastly, I will run the tests to make sure everything works as expected:
 βœ“ get-auth-token.test.ts (3)
   βœ“ returns the authentication token on successful authentication
   βœ“ throws an error if the user credentials are invalid
   βœ“ throws an error if the server responds with an error

Further reading

Runtime request handlers is a powerful way to describe dynamic network behaviors. I highly recommend you to read the πŸ“œ Network behavior overrides best practice to learn more how you can utilize it during developing and testing your projects.

Access Denied

You must login or register for the workshop to view the diff.

Check out this video to see how the diff tab works.