Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to test NextJS endpoints #84

Open
yegorkay opened this issue Jul 18, 2020 · 14 comments
Open

How to test NextJS endpoints #84

yegorkay opened this issue Jul 18, 2020 · 14 comments

Comments

@yegorkay
Copy link

yegorkay commented Jul 18, 2020

Looking at your other repo, how would you go about testing an endpoint that uses middleware, or endpoints, in general, using next-connect?

https://github.com/hoangvvo/nextjs-mongodb-app/blob/master/pages/api/users/%5BuserId%5D/index.js#L8

A potential test:

import { createServer } from 'vercel-node-server'
import listen from 'test-listen'
import axios from 'axios'
import handler from '.'

let server
let url

beforeAll(async () => {
  server = createServer(handler)
  url = await listen(server)
})

afterAll(() => {
  server.close()
})

it('should return the expected response', async () => {
  const response = await axios.get(url, { params: { userId: 'SOME_ID' } })
  expect(response.data).toBeTruthy()
})

Doing something like this returns a 500. Maybe it's just me who is lacking testing knowledge. Any help would be appreciated.

@hoangvvo
Copy link
Owner

hoangvvo commented Jul 19, 2020

These two can help https://www.npmjs.com/package/light-my-request or https://www.npmjs.com/package/supertest

Using the above, you don't have to start and close the server.

@yegorkay
Copy link
Author

yegorkay commented Jul 20, 2020

Thanks for the reply! I tried setting up a test for the file mentioned above, and realize it's the database and session middlewares causing the 500 error. 200 happens when you comment both of them out of here. You would then have to change the endpoint to not use Mongo like so to pass the test since the middleware isn't applied:

import nextConnect from 'next-connect';
import middleware from '../../../../middlewares/middleware';
import { getUser } from '../../../../lib/db';

const handler = nextConnect();

handler.use(middleware);

// dumb API without mongo
handler.get(async (req, res) => {
  res.send(`hello ${req.query.userId}`);
});

export default handler;
// working test, assuming no Mongo middleware applied
import http from 'http'
import fetch from 'isomorphic-unfetch'
import listen from 'test-listen'
import { apiResolver } from 'next/dist/next-server/server/api-utils'
import handler from '.';

const USER_ID = 'SOME_ID' // to be an actual id once 500 is resolved

describe(`/users/${USER_ID}`, () => {
  test('responds 200', async () => {

    const requestHandler = (req, res) => apiResolver(req, res, undefined, handler)
    const server = http.createServer(requestHandler)
    const url = await listen(server)

    const response = await fetch(`${url}/${USER_ID}`)
    expect(response.status).toBe(200)
    return server.close()
  })
})

Seems like this issue doesn't pertain to this repo, but more towards the nextjs-mongo-db-app repo. I can move this there. I've been trying to figure out how to get integration testing to work there, but I haven't been getting very far. Any ideas as to why mongo causes this to fail?

@hoangvvo
Copy link
Owner

hoangvvo commented Jul 25, 2020

Oh I see. This should have been mentioned clearer in next-connect doc. next-connect does not add anything by default so things like query parsing (req.query) and helpers like (res.send) are not available. That why errors were thrown in your case.

Nope, wrong assumption, you are using apiResolver so it should work. Did you manage to get the error message?

@yegorkay
Copy link
Author

Still getting a 500 sadly. Here are the changes I made if you want to see what's going on:

https://github.com/yegorkay/nextjs-mongodb-app/tree/feature/testing

@ljosberinn
Copy link
Contributor

ljosberinn commented Jul 31, 2020

@yegorkay hey sorry I didn't check in here for a while - I have a whole testing setup tailored for nextjs here:
https://github.com/ljosberinn/personal-react-boilerplate/blob/master/testUtils/lambda.ts

and a few tests: https://github.com/ljosberinn/personal-react-boilerplate/tree/master/src/server/__tests__

@leosuncin
Copy link

leosuncin commented Apr 22, 2021

My approach to test is https://github.com/leosuncin/literate/blob/master/tests/pages/api/auth/register.spec.ts
I use @shelf/jest-mongodb to run MongoDB in-memory, but basically I use node-mocks-http to mock req and res objects

@leosuncin
Copy link

Another idea is to use supertest

import http from 'http';

import { apiResolver } from 'next/dist/next-server/server/api-utils';
import supertest from 'supertest';

import registerHandler from 'pages/api/auth/register';

jest.setTimeout(10e3);

describe('[POST] /api/auth/register', () => {
  let server;

  beforeEach(async () => {
    const requestHandle = (request, response) =>
      apiResolver(
        request,
        response,
        undefined,
        registerHandler,
        {},
        true,
      );
    server = http.createServer(requestHandler);
  });

  afterEach(() => {
    server.close();
  });

  it('creates a new user', async () => {
    const body = {
      name: 'John Doe',
      email: 'johndoe@example.com',
      password: 'ji32k7au4a83',
    };
    const result = await supertest(server)
      .post('/api/auth/register')
      .send(body)
      .expect(201)
      .expect('Content-Type', /json/);

    expect(result.body).toBeDefined();
  });

  it.each([
    {
      name: '',
      email: '',
      password: '',
    },
    {
      name: 'a',
      email: 'email',
      password: '123456',
    },
  ])('validate the body %p', async (body) => {
    const result = await supertest(server)
      .post('/api/auth/register')
      .send(body)
      .expect(422)
      .expect('Content-Type', /json/);

    expect(result.body).toBeDefined();
  });
});

It has the advantage of no need to start a server

@mactkg
Copy link

mactkg commented Feb 2, 2022

#166 helps me when I write a test using node-mocks-http.
You can use #166 branch like this

npm i --save https://github.com/jakeorr/next-connect#c837eee

and add "postinstall" script to package.json

{
  "scripts": {
    "postinstall": "cd node_modules/next-connect; npm i; npm run build"
  }
}

@nath-green
Copy link

Is there a way to test with Jest?

This isn't my post but it is the same issue I'm facing: https://stackoverflow.com/questions/67961149/how-do-i-test-a-next-js-api-route-which-uses-next-connect

When using next-connect I get a timeout error. When using without, the tests work as expected.

Cheers!

@chrisrabe
Copy link

chrisrabe commented Sep 18, 2022

@nath-green I use next-connect and using leosuncin's solution worked for me. It no longer has the jest timeout issue

@TotomInc
Copy link

If someone need some guidance about how to write unit-tests (with Jest) for your middlewares, I've written a blog-post about it.

If you need to test an API route endpoint from end-to-end, I recommend using next-test-api-route-handler (ntarh)

@mtbrault
Copy link

I suggest you to use this pacakge: https://www.npmjs.com/package/nextjs-http-supertest.
It allows you to build an http server which will link endpoints with your nextJS handler.

@TotomInc
Copy link

I suggest you to use this pacakge: https://www.npmjs.com/package/nextjs-http-supertest. It allows you to build an http server which will link endpoints with your nextJS handler.

How is it better against ntarh? ntarh seems to be already established solution for Next.js endpoint unit-testing. It takes cares of all the boring unit-testing setup and doesn't use an HTTP server. It directly executes the endpoint function which seems to be more efficient and performant.

@mtbrault
Copy link

I suggest you to use this pacakge: https://www.npmjs.com/package/nextjs-http-supertest. It allows you to build an http server which will link endpoints with your nextJS handler.

How is it better against ntarh? ntarh seems to be already established solution for Next.js endpoint unit-testing. It takes cares of all the boring unit-testing setup and doesn't use an HTTP server. It directly executes the endpoint function which seems to be more efficient and performant.

As you said, ntarh is a solution for unit-testing.
My package is a solution for integration-testing, this is not the same thing.

In unit testing you are only verifying that you're handler is working well, but you don't know if the correct handler is reach for a given endpoint.
The aim of integration-testing, is to simulate the context of a browser or any other client performing an http request

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants