Utilizing NestJS to efficiently share an end-to-end server across multiple test suites

Currently, I'm utilizing the NestJS test module to simulate the nest app for testing purposes and my goal is to make this app accessible across various test suites.
Here is how I have set it up:

test
  |_ helpers
    |_ testApp.ts
  |_ e2e
    |_ users.e2e-test.ts
  |_ beforeAll.e2e-test.ts

testApp.ts

import { Test } from '@nestjs/testing';
import { DatabaseModule } from '../../src/database/database.module';
import { UserModule } from '../../src/user/user.module';

let app: any;
export async function initServer() {
  const fixture = await Test.createTestingModule({
    imports: [
      DatabaseModule,
      UserModule,
    ],
  }).compile();
  app = fixture.createNestApplication();

  await app.init();
}
export default app;

beforeAll.e2e-test.ts

import { initServer } from './helpers/testApp';

before(async () => {
  await initServer();
});

users.e2e-test.ts

import * as request from 'supertest';
import * as chai from 'chai';
const expect = chai.expect;

import { UserType } from '../../src/user/enum/user-types.enm';
import app from '../helpers/testApp';

const admin = {
  email: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="3c5d585155527c59445d514c5059125f5351">[email protected]</a>',
  password: '123',
  type: UserType.ADMIN
};
describe.only('Creating User with customized permissions', async () => {
  it('User should be able to sign in', async () => {
    request(app.getHttpServer())
      .post('/auth/signin')
      .send({ email: admin.email, password: '123' })
      .end((_, res) => {
        expect(res.status).to.equal(200);
      });
  });
});

My main objective is to share the NestApplication instance among different test suites. However, I am encountering an issue where the app is showing up as undefined in the test case, resulting in the error message:

TypeError: Cannot read property 'getHttpServer' of undefined

Is there a solution or workaround for this problem? Should I initialize a new NestApplication for each test suite?

Just to give you more context, I am utilizing mocha as my test runner.

Answer №1

I encountered a similar problem, and here is how I resolved it:

Here is my jest-e2e.json:

{
  "moduleFileExtensions": ["js", "json", "ts"],
  "rootDir": ".",
  "testEnvironment": "node",
  "testRegex": ".e2e-spec.ts$",
  "transform": {
    "^.+\\.(t|j)s$": "ts-jest"
  },
  "setupFilesAfterEnv": ["<rootDir>/setup.ts"]
}

This is my setup.ts file:

export let app: NestExpressApplication;

async function initializeServer() {
  const moduleRef = await Test.createTestingModule({
    imports: [
      AppModule,
      AuthModule,
      UsersModule
    ],

  }).compile();

  app = moduleRef.createNestApplication<NestExpressApplication>();
  app.useGlobalInterceptors(new TransformationInterceptor);
  app.useGlobalInterceptors(new TransformError);
  app.useGlobalPipes(
    new ValidationPipe({
      whitelist: true,
      transform: true,
      forbidNonWhitelisted: true,
      transformOptions: {
        enableImplicitConversion: true,
      },
    }),
  );

  const configService = app.get(ConfigService);
  // added for custom validator
  useContainer(app.select(AppModule), { fallbackOnErrors: true });
  await app.init();
  await app.listen(3002); 
}

global.beforeAll(async () => { 
  await initializeServer();  
});

Lastly, here is my test code in auth.e2e-spec.ts:

import {app} from '../setup';

Answer №2

Give this a shot.

beforeAll(async () => {
  const module = await Test.createTestingModule({
    providers: [
      MyTestsService,
      {
        provide: getRepositoryToken(Warehouse),
        useValue: myTestsService
      }
    ],
    controllers: [MyTestsController], // <-- including this line will solve the issue with getHttpServer
  })
    .overrideProvider(MyTestsService)
    .useValue(myTestsService)
    .compile();

  app = module.createNestApplication();
  await app.init();
});

Answer №3

After struggling with this folder structure in my testing process, I finally figured out how to efficiently export my app across different test suites. Hopefully, this solution will save you time and frustration, as I spent a good half hour battling the same issue.

    __test__
  |_ category.e2e-test.ts
  |_ test-conf.ts
**test-conf.ts**
   export default async function initializeTestServer() {
   let app: INestApplication;

  const moduleRef = await Test.createTestingModule({
    imports: [AppModule],
  }).compile();

  return moduleRef.createNestApplication();
}
**category.e2e-test.ts**
describe('CategoryController', () => {
    let app: INestApplication;

    beforeAll(async () => {
        app = await initializeTestServer();
        await app.init();
    });

    it('/ (GET)', async () => {
        const response = await request(app.getHttpServer())
            .get('/category')
            .expect(200)
        expect(response.body.length).toBeGreaterThan(0);
        expect(typeof response.body).toBeDefined();
        expect(response.body).toBeInstanceOf(Object);
        expect(Array.isArray(response.body)).toBeTruthy();
    });

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

This solution has resolved my issue for now, but please note that my tests currently run against my development database. If you need to conduct tests against your testing database, be sure to configure your testing database settings within the moduleRef of the test-conf.ts file. Feel free to reach out if you need guidance on this! Now, feel free to utilize the initializeTestServer() function in any test suite you require.

I hope you find this information helpful!

Answer №4

No way! You must run each tests suite in its own process! Sharing a NestApplication between different test suites is not feasible. The goal is to keep each test suite's environment separate. Even if you utilize global.beforeAll(), it simply means the code will execute at the beginning of each test suite. So, the more test suites you have, the more times it will run. If you really need to share NestApplication, consider writing all your test suites in a single file (even though it may not look pretty).

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

What is the most efficient way to iterate through an array to push properties into an object nested within another array?

I have been working on a small Angular application that functions as a scheduler, allowing users to input a Name, Start and End dates, and toggle a boolean checkbox through a form. One challenge I am facing is trying to assign the names entered by each use ...

Guide on generating a request through iteration using Javascript

I'm currently working on a request that involves multiple methods, and I want to streamline the code by using an enum to iterate through and construct the request. However, my attempt at doing this has resulted in unexpected outcomes. The original co ...

Encountering a problem with Vue StripeCheckout while navigating to a different component

I'm looking to integrate the StripeCheckout component into my Vue application. After copying and updating their example code using the composition API from here, everything works fine when I route to the subscribe component. However, if I try to navig ...

How can one dynamically update a page in Angular when the path is changed?

I am facing a pagination issue in Angular. Here is my HTML code: <!-- A div element for pagination part at the bottom of the page --> <div style="margin-left: 50%; margin-top: 20px; margin-bottom: 20px"> <ul class="paginat ...

Angular - Automatically hide sidebar menu upon selecting a menu item

Struggling to hide a sidebar menu after clicking on a menu item that you created? I ran into the same issue and tried following the example from a tutorial on Do I really need to call toggleMenu on (click) of every hyperlink in the HTML? If so, how do I i ...

Creating a dynamic multi-line list in Ionic application is a breeze!

I'm a beginner in Ionic and I am interested in creating a Multi-line List similar to how we generate list-views in Android with custom views and using an Array of custom objects programmatically. Can Multi-line Lists be generated with data from an Ar ...

Having trouble getting a constructor to function properly when passing a parameter in

Here's the code snippet I'm working with: import {Component, OnInit} from '@angular/core'; import {FirebaseListObservable, FirebaseObjectObservable, AngularFireDatabase} from 'angularfire2/database-deprecated'; import {Item} ...

Working with an arbitrary number of arguments in TypeScript

Looking for a way to pass an arbitrary number of arguments with different types into a function and utilize those types, similar to the following: function f<A>(a: A): A; function f<A, B>(a: A, b: B): A & B; function f<A, B, C>(a: A, ...

Using Angular 2 with Typescript to call a JavaScript function

How can I correctly invoke a JavaScript function from a component in Angular 2 (TypeScript)? Below is the code for my component: import { ElementRef, AfterViewInit } from '@angular/core'; export class AppComponent implements AfterViewIni ...

Registering modules with System.js in a typescript project involves a specific process

In my TypeScript project, I am trying to concatenate the compiled output into a single file. I am using the SystemJs module, but I am facing an issue where the output changes when I include an 'import' statement in the script files. For example, ...

What is the best way to extract a nested array of objects and merge them into the main array?

I've been working on a feature that involves grouping and ungrouping items. A few days ago, I posted this question: How can I group specific items within an object in the same array and delete them from the core array? where some helpful individuals ...

Executing an animation in Angular 4 using a Directive

There's an ongoing issue on the repository here, but I wanted to see if anyone here could help as well. I am trying to programmatically trigger an animation from a Directive. However, when using Renderer.animate, I receive the following error: Rende ...

Discovering Angular2 Heroes: Exploring the Purpose of the Colon in onSelect(hero: Hero)

Currently, I am working on the Angular2 tour of heroes project. You can check it out here. <li *ngFor="let hero of heroes" (click)="onSelect(hero)">{{hero.name}}</li> Within this project, I have a function that alerts the current hero's ...

The element is implicitly assigned an 'any' type due to the fact that an expression of type 'any' cannot be used to index types in nodejs and solidity

I am in need of setting networks in my contract using NodeJS and TypeScript. Below is the code I have written: let networkId: any = await global.web3.eth.net.getId(); let tetherData = await Tether.networks[networkId]; Unfortunately, I encountered ...

Tips on transferring information to the graphical user interface

Here is my code snippet: signup.post('/signup', urlendcodedParser, async(req: Request, res: Response) => { const username = req.body.username; const password = req.body.password; const age = req.body.age; const email = req ...

Generating a unique serial ID using Angular/JS

I am in the process of developing a function that will create a unique serial id by replacing a string with the format; xxxx-xxxx-xxxx-xxxx. The desired outcome is a serial like this: ABCD-1234-EFGH-5678, where the first and third parts consist of letters ...

The scenario involving TypeScript, MySQL, and Socket.IO where re-emitting occurs after a failed login attempt

I'm encountering an issue where, after failing to sign in and then successfully signing into my game, it creates a game instance for every failed login attempt. This same problem occurs with failed sign-up attempts as well. I would like to provide mo ...

Changing setState in React does not update the current state

My challenge lies in altering the value of a TreeSelect component from the antd (antdesign) UI library. I followed their instructions outlined in their documentation, with the only divergence being the use of Typescript. The issue I encounter is with ch ...

Angular 2 - Troubleshooting Issues with Nested Components and @Input Propagation

The Logic by IMG I'm facing an issue with updating child component variables in real-time. I attempted to use @Input, but it only gets initialized and doesn't change over time. Parent Component <div> <h4>Рэйтынг : {{video ...

Navigating through pages: How can I retrieve the current page value for implementing next and previous functions in Angular 7?

Greetings, I am a new learner of Angular and currently working on custom pagination. However, I am facing difficulty in finding the current page for implementing the next and previous functions. Can anyone guide me on how to obtain the current page value? ...