Brannar3
Brannar3

Reputation: 1

500 Internal Server Error in Jest Test for Express App Using Supertest

I'm encountering a 500 Internal Server Error when running a Jest test for my Express app using Supertest, but the app works fine when run normally (e.g., in a browser or Postman). I suspect the issue lies in the test setup, but I'm unable to pinpoint the exact cause.

This is the error message when running npm test

$ npm test

> [email protected] test
> cross-env NODE_OPTIONS=--experimental-vm-modules jest

(node:16440) ExperimentalWarning: VM Modules is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
 FAIL  tests/movies.test.js
  × Home page shows list of movies and navigation links (31 ms)

  ● Home page shows list of movies and navigation links

    expected 200 "OK", got 500 "Internal Server Error"

      33 |   );
      34 |
    > 35 |   const response = await request(app).get('/').expect('Content-Type', /html/).expect(200);
         |                                                                               ^
      36 |
      37 |   expect(response.text).toMatch('Pulp Fiction');
      38 |   expect(response.text).toMatch('Fire Walk With Me');

      at Object.expect (tests/movies.test.js:35:79)
      ----
      at Test._assertStatus (node_modules/supertest/lib/test.js:252:14)
      at node_modules/supertest/lib/test.js:308:13
      at Test._assertFunction (node_modules/supertest/lib/test.js:285:13)
      at Test.assert (node_modules/supertest/lib/test.js:164:23)
      at Server.localAssert (node_modules/supertest/lib/test.js:120:14)

Test Suites: 1 failed, 1 total Tests: 1 failed, 1 total Snapshots: 0 total Time: 0.617 s, estimated 1 s Ran all test suites.

I'll provide some of my code movies.test.js

import { expect, test } from '@jest/globals';
import request from 'supertest';
import { fetchAllMovies, fetchMovie } from '../src/js/server/fetchMovies.js';
import startApp from '../src/js/server/app.js';

test('Home page shows list of movies and navigation links', async () => {
  const app = startApp(
    {
      fetchMovie: async () => ({
        id: 1,
        title: 'Pulp Fiction',
      }),
      fetchAllMovies: async () => [
        { id: 1, title: 'Pulp Fiction' },
        { id: 2, title: 'Fire Walk With Me' },
        { id: 3, title: 'Isle of Dogs' },
      ],
    },
    {
      navLinks: async () => ({
        headerData: [
          { label: 'BILJETTER', id: 'biljetter', link: '#' },
          { label: 'EVENEMANG', id: 'evenemang', link: '#' },
          { label: 'FILMER', id: 'filmer', link: 'movies' },
        ],
        footerSection1: [
          { label: 'OM KINO', link: 'about' },
          { label: 'FRÅGOR SVAR', link: '#' },
          { label: 'KONTAKTA OSS', link: '#' },
        ],
      }),
    }
  );

  const response = await request(app).get('/').expect('Content-Type', /html/).expect(200);

  expect(response.text).toMatch('Pulp Fiction');
  expect(response.text).toMatch('Fire Walk With Me');
  expect(response.text).toMatch('Isle of Dogs');

  expect(response.text).toMatch('BILJETTER');
  expect(response.text).toMatch('EVENEMANG');
  expect(response.text).toMatch('FILMER');
});

server.js

import startApp from './src/js/server/app.js';
import { fetchAllMovies, fetchMovie } from './src/js/server/fetchMovies.js';
import navLinks from './src/js/server/navLinks.js';

const api = {
  fetchMovie,
  fetchAllMovies,
};

const nav = {
  navLinks,
};

const app = startApp(api, nav);

const PORT = process.env.PORT || 5080;
app.listen(5080, () => {
  console.log(`Server running on http://localhost:${PORT}`);
});

app.js

import express from 'express';
import ejs from 'ejs';
import { marked } from 'marked';

function startApp(api, nav) {
  const app = express();

  app.locals.formatMarkdown = (text) => marked(text);
  app.set('view engine', 'ejs');

  app.get('/', async (req, res) => {
    const linkData = await nav.navLinks();
    const moviesData = await api.fetchAllMovies();
    res.render('pages/index', { moviesData, ...linkData });
  });

  app.get('/movies', async (req, res) => {
    const linkData = await nav.navLinks();
    const moviesData = await api.fetchAllMovies();
    res.render('pages/allMovies', { moviesData, ...linkData });
  });

  app.get('/movie/:movieId', async (req, res) => {
    const linkData = await nav.navLinks();
    const movieData = await api.fetchMovie(req.params.movieId);
    res.render('pages/movie', { movieData, ...linkData });
  });

  app.use('/static', express.static('./static'));

  app.use((req, res) => {
    const linkData = nav.navLinks();
    res.status(404).render('pages/error', { message: 'Page not found', ...linkData });
  });

  return app;
}

export default startApp;

navLinks.js

const headerData = [
  {
    label: 'BILJETTER',
    id: 'biljetter',
    link: '#',
  },
  {
    label: 'EVENEMANG',
    id: 'evenemang',
    link: '#',
  },
  {
    label: 'FILMER',
    id: 'filmer',
    link: 'movies',
  },
  {
    label: 'MEDLEMSKAP',
    id: 'medlemskap',
    link: '#',
  },
  {
    label: 'OM OSS',
    id: 'omoss',
    link: 'about',
  },
];

const footerSection1 = [
  { label: 'OM KINO', link: 'about' },
  { label: 'FRÅGOR SVAR', link: '#' },
  { label: 'KONTAKTA OSS', link: '#' },
];

const footerSection2 = [
  { label: 'PRESENTKORT', link: '#' },
  { label: 'TILLGÄNGLIGHET', link: '#' },
  { label: 'MEDLEMSKAP', link: '#' },
  { label: 'BISTRO-BIO', link: '#' },
  { label: 'EVENEMANG', link: '#' },
];

const footerSection3 = [
  { label: 'FACEBOOK', link: '#' },
  { label: 'INSTAGRAM', link: '#' },
];

export default function navLinks() {
  return {
    headerData,
    footerSection1,
    footerSection2,
    footerSection3,
  };
}

Upvotes: 0

Views: 71

Answers (2)

noswoscar
noswoscar

Reputation: 1

There are many possible reasons you might be getting an Internal Server Error:

  1. The data you are passing to the app in your test is not well formatted or not a valid object (can you provide the src/js/server/fetchMovies.js file, Pages/index?)
  2. You manually allowed a security exception in your browser when first visiting your app like for example not using https but http. And then maybe your app doesn't allow Supertest to do the same thing. (does using curl work on your / endpoint? )
  3. Your res.render function might not render html

....other errors due to the specificity of the modules you imported

I am not exactly sure of the answer, I hope the above tips head you in the right direction. Please provide as many details as possible.

A good practice in your case when facing such a bug is to decouple all the things you are trying to use all at once and test them individually. This will help you find where your code went wrong.

Some tips to gain visibility:

  1. Do

const linkData = await nav.navLinks();

const moviesData = await api.fetchAllMovies();

outside of the individual routes. You can get the linkData and moviesData in the app. That way your /* routes only have to return html. Only one purpose that you can test and validate that it's not the source of the problem.

  1. Use the same fetchMovies and fetchAllMovies and navlinks in your app and in your tests. If they are different, that might be the reason one way it's running ok and the other it's failing.

  2. Separate your logic functions from your routes and test them one by one by logging in the app what they get.

  3. Test your render function without the logic with test html

  4. Make sure 'Content-Type', /html/ is correct

  5. Change to different modules that do the same thing for you, see what error you get.

Please get back to me at any stage with more information. I would be happy to help further.

Best

Upvotes: 0

Brannar3
Brannar3

Reputation: 1

The issue stemmed from missing data in the test that was required for rendering the footer section in the EJS template. Specifically, the footerSection2 and footerSection3 data were not provided in the mock data for the test. This resulted in forEach loops inside my partials (footer) not rendering correctly during the GET request.

Upvotes: 0

Related Questions