I am trying to run a unit test for a basic app api get route in Next.js. I am using Mysql as the database and Prisma as the ORM. Typescript is the language being used. Originally, I was getting an error telling me that response was not defined when I would try running npm test. Someone alerted me to the fact that jest has the tendency to remove gloable variables and fails to re-add them. The mock service worker docs recommend creating a jest.pollyfills.js file to re-add response to the node.js global variables. I now get an error stating:
API route returned a Response object in the Node.js runtime, this is not supported. Please use 'runtime: "edge"' instead: https//nextjs.org/docs/api-routes/edge-api-routes at apiResolver (node_modules\next\src\server\api-utils\node\api-resolver.ts:432:17
I've tried using runtime: edge according to the next.js docs, but I still get the same error. I've tried adding
export const runtime = 'edge'
to my route.ts file, and
runtime: "edge"
to my next.config.js file
Here is my route.ts file to handle the api route
import { db } from "@/lib/db";
import { PrismaClient, Prisma } from "@prisma/client";
import { useSearchParams } from "next/navigation";
import { NextResponse, NextRequest } from "next/server";
import type { NextApiRequest, NextApiResponse } from 'next'
export const runtime = 'edge'
const prisma = new PrismaClient();
// This works when using Postman and running the server, but doesn't pass the test.
export async function GET() {
try {
const platform = await prisma.platform.findMany();
return NextResponse.json({ success: true, platform }, { status: 200});
} catch (error) {
if (error instanceof Error) {
console.log(error.message);
return NextResponse.json({ success: false }, { status: 500 });
}
}
}
Here is my route.test.ts file to run the test with supertest.
//import request from 'supertest';
//import server from 'nextjs-http-supertest';
import { GET } from "./route";
import { testClient } from "../../../../utils/test-client";
const request = testClient(GET);
describe("platform model", () => {
it("should return expected data", async () => {
const response = await request.get("/api/platform/findMany");
//console.log(response);
expect(response.status).toEqual(200);
})
});
I tried using the nextjs-http-supertest package but it didn't work for me Simply starting the server and running the test does work, but I'm trying to run the unit test without having to start the server.I know I have request import from supertest commented out. The testClient file has "request" imported from supertest.
Here is my testClient.ts file
import request from 'supertest';
import { createServer, RequestListener } from "http";
import { NextApiHandler } from "next";
import { apiResolver } from "next/dist/server/api-utils/node/api-resolver";
import { NextRequest, NextResponse } from 'next/server';
export const testClient = (handler: NextApiHandler) => {
const listener: RequestListener = (req, res) => {
return apiResolver(
req,
res,
undefined,
handler,
{
previewModeEncryptionKey: "",
previewModeId: "",
previewModeSigningKey: "",
},
false
);
};
return request(createServer(listener));
};
Here is my jest.config.ts file
import type {Config} from 'jest';
import nextJest from 'next/jest.js';
const createJestConfig = nextJest({
// Provide the path to your Next.js app to load next.config.js and .env files in your test environment
dir: './',
})
// Add any custom config to be passed to Jest
const config: Config = {
// Automatically clear mock calls, instances, contexts and results before every test
clearMocks: true,
// Indicates whether the coverage information should be collected while executing the test
collectCoverage: true,
coverageProvider: 'babel',
// The directory where Jest should output its coverage files
coverageDirectory: "coverage",
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
// A preset that is used as a base for Jest's configuration
preset: 'ts-jest',
// The paths to modules that run some code to configure or set up the testing environment before each test
setupFiles: ['./jest.polyfills.js'],
// A list of paths to modules that run some code to configure or set up the testing framework before each test
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
// The test environment that will be used for testing
testEnvironment: 'jsdom',
};
export default createJestConfig(config);
And here is my jest.polyfills.js fill to re-add the Response variables to node.js
// https://mswjs.io/docs/faq
// jest.polyfills.js
/**
* @note The block below contains polyfills for Node.js globals
* required for Jest to function when running JSDOM tests.
* These HAVE to be require's and HAVE to be in this exact
* order, since "undici" depends on the "TextEncoder" global API.
*
* Consider migrating to a more modern test runner if
* you don't want to deal with this.
*/
const { TextDecoder, TextEncoder } = require("node:util");
const { ReadableStream, TransformStream } = require("node:stream/web");
Object.defineProperties(globalThis, {
TextDecoder: { value: TextDecoder },
TextEncoder: { value: TextEncoder },
ReadableStream: { value: ReadableStream },
TransformStream: { value: TransformStream },
})
const { Blob, File } = require('node:buffer')
const { fetch, Headers, FormData, Request, Response } = require('undici')
Object.defineProperties(globalThis, {
fetch: { value: fetch, writable: true },
Blob: { value: Blob },
File: { value: File },
Headers: { value: Headers },
FormData: { value: FormData },
Request: { value: Request },
Response: { value: Response },
})
Ultimately, I would like for the unit test to pass, but the current error I'm receiving is that the Response object is being returned in Node.js runtime instead of the "edge" runtime. Any help would be appreciated. Thank you for your time!