Assert for correct body with mock service worker

196 views Asked by At

we got a Vue application which calls an api endpoint using vueuses useFetch. It is tested with Vitest and msw (mock service worker) to check our API calls. Our code looks like the following:

// api.ts
async function postEndpoint: Promise<void> {
  const body = complexObject
  const { error } = await useFetch<MyDto>('/api/endpoint').post(body)

  if (error.value) throw new Error(error.value)
}

As you can see the happy path does not return any value. However, we need to make sure wheter the api endpoint has been called with the correct body. Therefore we want to check spy on fetch:

  describe('when the contact form is posted', () => {
    beforeEach(() => {
      fetchSpy = vi.spyOn(window, 'fetch')
      server.use(
        rest.post('/api/endpoint', (req, res, ctx) => {
          return res(ctx.status(201))
        })
      )
    })

    it('should call the endpoint ', async () => {
      const result = await postEndpoint(contactForm)

      // TODO - assert fetch is using the correct body
      expect(fetchSpy).toHaveBeenCalledWith(
        '/api/endpoint',
        expect.objectContaining({ body: JSON.stringify(complexObject)})
      )
    })
})

However our assertion does not work correctly, because the string serialization of the complex object does not fit with the actual posted body-string. It has the same content, but the order of the members differ.

How can we fix this test and ensure wheter the api has been called with the correct body even if our api-method postEndpoint has no return value??

1

There are 1 answers

1
kettanaito On BEST ANSWER

Although I generally discourage against request assertions, I leave you to be the judge of whether that's necessary in your tests.

Here's how you can get the request body and assert it in your test:

import { DeferredPromise } from '@open-draft/deferred-promise'

afterEach(() => {
  // We will be using "server.use()", so don't
  // forget to reset the handlers between tests.
  server.resetHandlers()
})

it('calls the endpoint', async () => {
  const requestPromise = new DeferredPromise()

  server.use(
    // Create a runtime request handler to get
    // notified when a particular request happens.
    rest.get('/api/endpoint', (req, res, ctx) => {
      // Resolve the request promise with the request object.
      requestPromise.resolve(req)

      // I'm assuming you have a response mock defined
      // in your happy paths, so don't return anything
      // from this runtime handler.
    })
  )

  // Wait for the request promise to resolve.
  const request = await requestPromise
  // Assert anything on the intercepted request.
  // Here, let's assert the request's JSON body.
  expect(request.body).toEqual({ this: 'that' })
})

This example uses DeferredPromise to simplify request promise declaration.