'How to test nextjs endpoint using Jest?

I have an endpoint that handles user signup:

import { createToken } './token'; // Unable to mock
import { sendEmail } './email'; // Unable to mock

export default async function signUp(
  req: NextApiRequest,
  res: NextApiResponse
): Promise<any> {
  try {
    // Generate a verification token
    const token = await createToken(req.user);

    // Email the token
    await sendEmail(req.user, token);

    return res.status(200).send({ done: true });
  } catch (error: any) {
    console.error(error);
    return res.status(500).end(error.message);
  }
}

How do I mock the imported dependencies for my jest unit tests?

import signup from './signup';

describe('signup', () => {
  it('should return success', async () => {
    const req: IncomingMessage = {} as unknown as IncomingMessage;
    const res: ServerResponse = {
      end: jest.fn(),
    } as unknown as ServerResponse;

    const actual = await signup(req, res);

    ...
  });
});

Is it the case that Jest cannot actually mock these nested dependencies and some sort of DI pattern needs to be implemented here in the endpoint? If so, what DI patterns can I use to support unit tests for Nextjs endpoints?



Solution 1:[1]

It looks like jest.mock doesn't support mocking dependencies for imports outside of the actual test file (.spec.ts & .test.ts). In my case I have a lot of dependencies in my Next API endpoint.

I opted to use Nextjs's middleware pattern to test my endpoint logic instead.

// Helper method to wait for a middleware to execute before continuing
// And to throw an error when an error happens in a middleware
function runMiddleware(req: NextApiRequest, res: NextApiResponse, fn: Function) {
  return new Promise((resolve, reject) => {
    fn(req, res, (result: any) => {
      if (result instanceof Error) {
        return reject(result)
      }

      return resolve(result)
    })
  })
}

Extract the endpoint logic to the function and instead test only the extracted function.

// We test this function instead of the endpoint
export function post(
  _createToken: Function,
  _sendEmail: Function,
) {
  return async function(req: NextApiRequest, res: NextApiResponse) {
    ...
  }
}

Here is an outline for the test. My actual code depends on classes instead of functions but they amount to similar things.

describe('signup', () => {
  let createToken: Function;
  let sendEmail: Function;

  beforeAll(() => {
    createToken = jest.fn();
    sendEmail = jest.fn();
  });

  it('should complete the happy path', async () => {
    const req: NextApiRequest = {} as unknown as NextApiRequest;
    const res: NextApiResponse = {} as unknown as NextApiResponse;

    await (
      await post(createToken, sendEmail)
    )(req, res);

    // expect
  });
});

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1