1

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??

2
  • Do it the other way around - parse the string and compare it to the object, rather than serialising the object and comparing it to the string. Also note that you can get the body (and other properties of the request) from MSW, you don't need to spy on fetch (example). Commented Oct 19, 2023 at 8:51
  • Your second suggestions seems to be handy, thanks for sharing! However, with this approach it wont be possible to track how many times the request has been called. If needed, we have to use a spy. Commented Oct 19, 2023 at 9:45

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for your response. We already have seen this in the documentation!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.