nico.fyi
    Published on

    How to unit test Next.js API route

    Turned out it's not that hard!

    Authors

    Zaher Ghaibeh asked me how to unit test a Next.js API route. But he ended up figuring it out for himself. And he's kind enough to share his solution with me in this repository.

    His solution is to use Vitest and MSW (Mock Service Worker). Vitest is a test runner for JavaScript and TypeScript that makes it easy to write and run tests against your code. MSW is a mocking library that allows you to mock HTTP requests and responses in your tests.

    The idea is quite simple:

    1. Create a dummy request based on the NextRequest.
    2. Pass the mock request to the handler of the API route (GET, POST, etc).
    3. Assert that the response is what you expect.
    4. If necessary, intercept any requests made within the handler and mock them using MSW.

    I then tried it out on this site. I added a test for the API route that stores the data in the Turso database for the Should you use Vercel tool using the following steps:

    1. Install vitest and msw.
    2. Create a setup file for vitest. In this file I instantiate the MSW node server which will intercept all requests made within the API route handler.
    3. Create a test for the API route in the route.test.ts file.

    When the data is submitted, the request handler verifies that the reCAPTCHA token is valid by sending a request to Google's reCAPTCHA API. If the token is valid, the request handler will then continue to store the data in the Turso database. So in this case, I need to intercept the request to Google's reCAPTCHA API and mock the response to return success: true or success: false based on the test case.

    After that, I made sure that the Turso client instance connects to the in-memory database instead of the real database by setting the :memory: value for the TURSO_DB_URL environment variable.

    A few things to note based on my initial experience with Vitest and MSW:

    1. Don't use bun run test to run the tests. As of this writing, MSW doesn't seem to be able to intercept the requests when running the tests with bun. Use npm run test instead.
    2. You must explicitly tell Vitest where to find the environment file.
        env: loadEnv('test', process.cwd(), ''),
    
    1. If the function to be tested is async, you must use await when calling the expect function:
    await expect(() => POST(req)).rejects.toThrow()
    
    1. If you use the module path aliases in your test file, as I did here, you need to add them to the vitest config file:
    resolve: {
      alias: {
        '@/__tests__': fileURLToPath(new URL('./__tests__', import.meta.url)),
      },
    },
    

    Check out the related files for testing the API route on this website:

    1. vitest.config.ts
    2. setup.ts
    3. route.test.ts
    4. .env.test

    Zaher has written a lot about DevOps like Docker and Kubernetes in his blog. Check it out if you're interested in these topics!


    By the way, I'm making a book about Pull Requests Best Practices. Check it out!