TL;DR
- You do want to test your NextJS API Routes without running the server.
- You do use
next-connect
as your app router or middleware.
My main reason to use this approach is because that I want to use next-connect
and test them without running the server at all.
If you're not using this package (next-connect
) and you have a regular API route handler, then you perhaps use simpler (as I assume) approach with node-mocks-http
and it would be fine.
I could not make next-connect
and node-mocks-http
works together and it keep gives me undesired behavior. So, here is my way to solve it.
Here's all useful references from people that helps me to solve this problem:
-
Sean Connolly - Unit testing Next.js API routes
Article about integrating and testing Next's API routes with
node-mocks-http
andjest
. -
DEV Community - James Harvey's comment
I thought that James Harvey's solution and implementation is pretty conscise and neat. And I use his implementation for this approach.
yarn add supertest jest @types/jest
supertest
: for testing NodeJS's HTTP server with fluent API.jest
: to run our test suites / specs.@types/jest
: (optional) to help our IDE (i.e. VS Code) for jest's type acquisition
Build Dependency
yarn add next-connect
next-connect
: (optional) allows us to use router and middleware in our NextJS API Route.
-
Install all packages (build and dependencies)
-
Create or write
.babelrc
file (optional){ "env": { "test": { "presets": [ [ "next/babel", { "preset-env": { "modules": "commonjs" } } ] ] } } }
It will allow you to use ES6
import
-export
statement in your test files. -
Create or write
jsconfig.json
file (only if you have@types/jest
installed){ "typeAcquisition": { "include": [ "jest" ] } }
-
Write this code somewhere in your project and import to test your API Route handler (an example).
// lib/test-utils.js import { createServer } from "http"; import { apiResolver } from "next/dist/server/api-utils"; import supertest from "supertest"; /** * @type {import("next").NextApiHandler} handler */ export function testClient(handler) { const serverRequestListener = async (req, res) => { return apiResolver( req, res, undefined, handler, {}, undefined ); }; const server = createServer(serverRequestListener); return supertest(server); }
-
Use that helper function to test our API route handler (an example).
// __test__/hello-api.test.js import { testClient } from "../lib/test-utils"; import handler from "../pages/api/hello"; describe("GET /api/hello", () => { it("Returns 200 OK status", async () => { // <-- notice the async await testClient(handler) .get("/api/hello") .expect(200); // <-- supertest's API }); });
-
And you're done!