#integration testing: jest global setup

16 messages Β· Page 1 of 1 (latest)

tawny ocean
#

If I want to write integration for each module, Instead of initialising it for each module, I could have a global setup with jest and I could start writing the tests directly and the server would start only once instead every time for each module and I could just clear my database before each test cases run

to have a global setup with jest, just AppModule needs to be imported with other configs and modules like winston, Config, the envs etc.
Please help me with the global setup.

plush dew
#

afair you can't have dynamic shared global objects between tests, such as database connection or module/application

#

I would recommend making your test that make environment (preconditions such as objects in database) and clean after they finish

#

creating a helper class with function that create application and configure objects would be very helpful

tawny ocean
#
src
β”œβ”€β”€ auth
β”‚   β”œβ”€β”€ auth.controller.ts
β”‚   β”œβ”€β”€ auth.module.ts
β”‚   β”œβ”€β”€ auth.service.ts
β”‚   └── test
β”‚       β”œβ”€β”€ auth.e2e-spec.ts
β”œβ”€β”€ apikey
β”œβ”€β”€ test
β”‚   β”œβ”€β”€ jest-e2e.json
β”‚   └── app.e2e-spec.ts
β”œβ”€β”€ ...other modules
β”œβ”€β”€ package.json
β”œβ”€β”€ nest-cli.json
β”œβ”€β”€ tsconfig.json
└── ... other config files

This is my project structure, if I can somehow harness the jest's global setup or setupFilesAfterEnv structure, I could start the AppModule in the app.e2e-spec.ts where I can import the AppModule and start the server and I can just begin writing tests in auth.e2e-spec.ts

When I try to achieve that, I somehow endup coming with unforeseen errors and I am struggling to achieve it.

plush dew
plush dew
tawny ocean
#

No offence, but it wouldn't be a generalized approach, one more straight forward solution would be to start the server, and use the APP_URL variable, then use it with supertest as

const request = supertest(APP_URL)
...

request.post(API).expect(XYZ);
#

Although I wanna achieve without having to start and keep the server at another port, it should just instantiate the server and perform the test and close gracefully

plush dew
plush dew
tawny ocean
#

I solved it

dirctory structure

src
β”œβ”€β”€ auth
β”‚   β”œβ”€β”€ auth.controller.ts
β”‚   β”œβ”€β”€ auth.module.ts
β”‚   β”œβ”€β”€ auth.service.ts
β”‚   └── test
β”‚       β”œβ”€β”€ auth.e2e-spec.ts
β”œβ”€β”€ test
β”‚   β”œβ”€β”€ jest-e2e.json
β”‚   β”œβ”€β”€ setup.ts
β”‚   β”œβ”€β”€ mocks.ts
β”‚   └── app.e2e-spec.ts
β”œβ”€β”€ types
β”‚   β”œβ”€β”€ global.d.ts
β”œβ”€β”€ ...other modules
β”œβ”€β”€ package.json
β”œβ”€β”€ nest-cli.json
β”œβ”€β”€ tsconfig.json
└── ... other config files

jest-e2e.json

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

test/setup.ts

import { Test, TestingModule } from "@nestjs/testing";
import { AppModule } from "../src/app.module";
import { MailerService } from "@nestjs-modules/mailer";
import { mockMailerService } from "./mocks";
import { DataSource } from "typeorm";
import { ClassSerializerInterceptor, INestApplication, Logger, ValidationPipe } from "@nestjs/common";
import { Reflector } from "@nestjs/core";

let dataSource: DataSource;

beforeAll(async () => {
  const app = await initializeApp();

  global.app = app;
});

// beforeEach(async () => {
//   await resetDatabase();
// });

afterEach(async () => {
  await resetDatabase();
});

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

// Initialization function
async function initializeApp(): Promise<INestApplication> {
  const moduleFixture: TestingModule = await Test.createTestingModule({
    imports: [AppModule],
  })
    .overrideProvider(MailerService)
    .useValue(mockMailerService)
    .compile();

  const app: INestApplication = moduleFixture.createNestApplication();

  app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));
  app.useGlobalPipes(new ValidationPipe({ transform: true }));
  app.useLogger(false);

  await app.init();

  return app;
}

async function resetDatabase() {
  if (!global.app) throw new Error("app not initialized");

  dataSource = global.app.get<DataSource>(DataSource);
  const entities = dataSource.entityMetadatas;

  for (const entity of entities) {
    const repository = dataSource.getRepository(entity.name); // Get repository
    // await repository.query(`TRUNCATE TABLE "${entity.tableName}" RESTART IDENTITY CASCADE;`); // Truncate the table and restart identity
    await repository.clear(); // truncate the table
  }
}

test/mocks.ts

export const mockMailerService = {
  sendMail: jest.fn(),
};
#

types/global.d.ts

import { INestApplication } from "@nestjs/common";

declare global {
  var app: INestApplication | undefined;
}