Search code examples
node.jsunit-testingfetch-apifastifyvitest

How can I test a Fastify endpoint consuming a different endpoint?


I have a Fastify service, which I am testing with Vitest. Before each test, I am using fastify.inject to simulate the HTTP requests.

it('should respond with 200', async () => {
    const app = build()
    const response = app.inject({
      method: 'GET',
      url: '/first',
    })

    expect(response.statusCode).toStrictEqual(200)
})

This works fine, however now my service needs to call a different endpoint. So, when I do a GET on http://localhost:3000/first, it does a GET on /second and returns 200. This works as expected, but my test is failing. In the test, the request to http://localhost:3000/second always fails and the entire thing returns 500.

This works as expected in practice, but my test suite is failing. Two things I've noticed:

  1. I've inspected the response body of /first it reports an error "Only absolute URLs are supported" – but this is an absolute URL.
  2. When running a debugger, I can see that it enters the /second service, and everything seems to be in order here – but the fetch still fails in the /first service.

How do I simulate the second call?

Here is how my handlers look (they do more, but I've stripped them down just to isolate the issue):

const handler = async () => {
    const secondResponse = await fetch(
        `http://localhost:3000/second`,
    )

    // Never reaches next line
    const secondData = await secondResponse.json()

    return {
        data: secondData
    }
}

const secondHandler = async () => {
    // A debugger here suggests this handler is executed and returns
    return await {
        data: true
    }
}

Solution

  • Sorry for the brevity of the answer but I'm on mobile

    Why isn't it working?

    When you use "inject" and "ready" in Fastify's test suite, the HTTP server does not start. So, the fetch function can't get an answer because localhost:3000 is not up and running. This is the fastify design because it enables to run multiple test in parallel and it is faster (because as said the http server is not started).

    To solve your issue you need to call the listen method on your test suite - quick fix but not suggested.

    The second suggestion is to refractor your code and avoid to use fetch: you may call inject in your handler function instead (seen it already in production) or move the second handler to a fooHandler function and call it in your first handle like a simple function (that said, the second handler must not use reply.send()).

    Note the difference:

    • fetch does an http round trip to call the second handler so it is very slow
    • using inject instead is much more faster and it will trigger all the hooks registered
    • using the second handler as a simple function, does not trigger any hooks, so it is even more faster

    I talk about how to reuse handlers in my fastify book, checkout my profile if you want 👍