Initiate and terminate server using supertest

I've developed a server class that looks like this:

import express, { Request, Response } from 'express';

export default class Server {
  server: any;

  exp: any;

  constructor() {
    this.exp = express();
    this.exp.get('/', (_req: Request, res: Response) => {
      res.json('works');
    });
  }

  start(): void {
    this.server = this.exp.listen(3000);
  }

  stop(): void {
    this.server.close();
  }
}

For end-to-end testing, I'm utilizing supertest. My goal is to initiate my application beforeAll tests and terminate it once the tests are completed.

While it's straightforward to achieve this using beforeAll and afterAll by instantiating the Server class and invoking the start and close methods, I have numerous controllers to test, making it inconvenient to start and stop the server for each test file.

After exploring the documentation, I came across the setupFiles and setupFilesAfterEnv, but I struggled with stopping the server since the instance isn't shared between the two files.

Here's an example of a test file:

import supertest from 'supertest';

describe('Album Test', () => {
   let app: App;

   beforeAll(async (done) => {
     app = new App();

     await app.setUp(); // establishing database connection
     done();
   });

   afterAll(async (done) => {
     await app.close();

     app.server.stop();
     done();
   });

  const api = supertest('http://localhost:3000');

  it('Hello API Request', async () => {
    const result = await api.get('/v1/user');
    expect(result.status).toEqual(200);
    ...
  });
});

Although this approach works well, I find myself duplicating these beforeAll and afterAll methods in every test file. Is there a way to declare them only once?

Thank you

Answer №1

Give this code a try for successful results

const request = require('request')
const app = require('../../app')

describe('test::app', function(){

  let server = null
  let req = null

  before(function(done){
    server = app.listen(done)
    req = request.agent(server)
  })

  after(function(done){
    server.close(done)
  })

  it('should access /api/v1/data/12345 endpoint', function(){
    return req.get('/api/v1/data/12345')
      .expect(200, { result: {} })
  })

})

Answer №2

If you want to globally set up test fixtures, you can utilize the setupFiles feature. This allows you to define variables that can be accessed across multiple test files by assigning them to Node.js' global object.

For example:

app.ts:

import express, { Request, Response } from 'express';

export default class Server {
  server: any;
  exp: any;

  constructor() {
    this.exp = express();
    this.exp.get('/', (_req: Request, res: Response) => {
      res.json('works');
    });
  }

  start(): void {
    this.server = this.exp.listen(3000);
  }

  stop(): void {
    this.server.close();
  }
}

app.setup.js:

const App = require('./app').default;

beforeAll(() => {
  global.app = new App();
  global.app.exp.set('test setup', 1);
  console.log('app setup');
});

afterAll(() => {
  console.log('app stop');
});

jest.config.js:

module.exports = {
  preset: 'ts-jest/presets/js-with-ts',
  testEnvironment: 'node',
  setupFilesAfterEnv: [
    './jest.setup.js',
    '/Users/ldu020/workspace/github.com/mrdulin/react-apollo-graphql-starter-kit/stackoverflow/61659975/app.setup.js',
  ],
  testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
  verbose: true,
};

a.controller.test.js:

describe('controller a', () => {
  it('should pass', () => {
    console.log('test setup:', global.app.exp.get('test setup'));
    expect(1 + 1).toBe(2);
  });
});

b.controller.test.js:

describe('controller b', () => {
  it('should pass', () => {
    console.log('test setup:', global.app.exp.get('test setup'));
    expect(1 + 1).toBe(2);
  });
});

Here are the unit test results:

 PASS  stackoverflow/61659975/a.controller.test.js
  controller a
    ✓ should pass (5ms)

  console log
    app setup

      at Object.<anonymous> (stackoverflow/61659975/app.setup.js:6:11)

  console log
    app setup

      at Object.<anonymous> (stackoverflow/61659975/app.setup.js:6:11)

  console log
    test setup: 1

      at Object.<anonymous> (stackoverflow/61659975/b.controller.test.js:3:13)

  console log
    test setup: 1

      at Object.<anonymous> (stackoverflow/61659975/a.controller.test.js:3:13)

  console log
    app stop

      at Object.<anonymous> (stackoverflow/61659975/app.setup.js:10:11)

  console log
    app stop

      at Object.<anonymous> (stackoverflow/61659975/app.setup.js:10:11)

 PASS  stackoverflow/61659975/b.controller.test.js
  controller b
    ✓ should pass (3ms)

Test Suites: 2 passed, 2 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        6.749s, estimated 12s

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Multiple requests were made by Ajax

I am facing an issue with my ajax code where it is being called multiple times. Below is my php code: (While loop extracts results from a database. For brevity, I have included just the modal and dropdown menu sections.) $rezPet = mysqli_query($kon, "SEL ...

The functionality of allowEmpty : true in gulp 4.0 does not seem to be effective when dealing with

gulp.task("f1", () => { gulp.src([], {"allowEmpty": true}) .pipe(gulp.dest(location)); }) An error message pops up saying "Invalid glob argument:" when the code above is used. gulp.task("f1", () => { gulp.sr ...

Converting a jQuery function into AngularJS: A step-by-step guide

I'm completely new to AngularJs and I recently created a slider function using jQuery. Now, my goal is to convert this function into Angular. Below is the code snippet that I have: <div class="slide-container"> <div class="slide-s ...

Omit a specific page from the primary Next.js layout within the application directory

In my project, I have a main layout file located at /app/layout.tsx and separate authentication pages. I want the authentication pages to have their own custom layout defined in the file /app/auth/layout.tsx. The issue I am facing is that the main layout ...

Conflict in Vue.js between using the v-html directive and a function

Below is the component template for a notification: <template> <div> <li class="g-line-height-1_2"> <router-link :to="linkFromNotification(item)" @click.native="readNotification(item)" v-html="item. ...

Executing the AngularJS nested controller only once

I am still new to Angularjs and just started working on an app that has several "projects" each with a local menu displayed on specific pages. The main navbar with footer is located in the Index.html: <body ng-app="ysi-app" ng-controller="MainControlle ...

Which is better: Utilizing Ajax page echo or background Ajax/direct HTML manipulation?

I am facing a dilemma and I could really use some guidance. Currently, I am in the process of developing an ordering system using PHP/Smarty/HTML/jQuery. The main functionality revolves around allowing sellers to confirm orders on the site. My goal is to ...

Error: Property cannot be read after page refresh or modification

Upon refreshing or running the project for the first time, I encounter the error: TypeError: Cannot read property 'statements' of undefined This issue is perplexing as the data renders correctly but it appears that the connection is failing. ...

Error Message: Module not found while using Node Express with TypeScriptIssue: A

I have set up a straightforward node express app using TypeScript. My goal is to implement an errorMiddleware in the index.ts file. As I try to start the server, I encounter the following error: at Module.require (node:internal/modules/cjs/loader:100 ...

Express-session does not generate session instances

Currently, I am working on adding session functionality to my personal project using express-session and connect-mongo for storing sessions. Here is what I am hoping to achieve: The browser should receive a cookie after receiving the server's respon ...

Is there a way to set columns as initially hidden in MaterialTable?

I have a MaterialTable with many columns, and some of them are not always necessary to display. I am looking for a way to hide these columns unless the user specifically selects them. I attempted to use the hidden: true property in the column configuratio ...

Error: The object is not defined (evaluating '_$$_REQUIRE(_dependencyMap[32], "react-native-safe-area-context").SafeAreaView')

I am currently working on developing a chat application using react-native with the following dependencies: "dependencies": { "@react-native-async-storage/async-storage": "~1.17.3", "@react-native-community/masked ...

Display Content in a DIV When Form Field is Unfocused

After hours of searching, I still haven't found a solution! I am trying to create a form field (text) where users can type in text. I want the text they enter to appear in a div on the screen either as they type or after they finish typing. Maybe thi ...

Steps to Embed an Image File in a MERN Stack Application

I'm attempting to load an image from a file inline because I need to pass data (the image name), but nothing seems to be working. It doesn't work whether the image is inside the src folder or outside in the public folder. Here's what I trie ...

Ajax request missing Github Basic OAuth token in authentication process

My personal access token is not being passed to the request when I make an ajax call. I keep receiving an error message saying API rate limit exceeded for 94.143.188.0. (But here's the good news: Authenticated requests get a higher rate limit.. I atte ...

Enhance DataTables functionality by including the ability to select which script to execute

Currently, I have a DataTables displayed with the provided code, utilizing server-side processing which is functioning properly. I am interested in implementing a dropdown menu above the table that allows users to select from options such as: Product Gr ...

Despite implementing CORS, my POST request to expressjs is still not functioning properly

I have a function in my component that is responsible for sending a POST request to an express API: onSubmit (evt) { evt.preventDefault(); //alert(JSON.stringify(this.description)); axios.post('http://localhost:3000/api/v1/addCom ...

Trigger event once item is selected from the jQuery combobox dropdown

I have implemented a custom autocomplete combobox using the jQuery UI library to create text fields with dropdown functionality. This hybrid input allows users to either select an item from the dropdown or enter free text manually. However, I need to trigg ...

Breaking down the Express Js initialization file into separate modules

I've been struggling to organize my ExpressJS start-up file into modules, and it's not going well. I'm trying to split the file in a way that makes sense, but when I try to launch the server, I get no response at the home address request an ...

From integrating Vue 2 TSX to React TSX: ensuring seamless cooperation

Currently, my app is built with Vue 2 using vue-tsx-support, vue-class-component, and vue-property-decorator. All of my Vue 2 components are already TSX classes. I am interested in gradually transitioning to React. I experimented with https://github.com/ ...