Injection not working on E2E test with nestJs and node test runner

42 views Asked by At

I'm currently starting a new project with nestJs and the node test runner. My test service is working, but my e2e test is not.

Here is my controller :

import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {
    console.log(appService)
  }

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
}

here my service :

import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }
}

And my module :

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Finally my e2e test :

import { Test } from '@nestjs/testing';
import test from 'node:test';
import request from 'supertest';
import {INestApplication} from "@nestjs/common";
import * as assert from "assert";
import {AppModule} from "src/demo/app.module";

test.describe('AppController.getHello', () => {
  let app: INestApplication;

  test.before(async () => {
    const moduleFixture = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = moduleFixture.createNestApplication();
    await app.init();
  });
  test.after(async () => {
    await app.close();
  })

  test.it('should return "Hello World!"', async () => {
    await request(app.getHttpServer())
        .get('/')
        .then((response) => assert.strictEqual(response.body, 'Hello World!'))
  });
});

And here is the error when I launch the tests with glob ./**/*.test.ts -c 'tsx --test' :

TypeError: Cannot read properties of undefined (reading 'getHello')
    at AppController.getHello (.../app.controller.ts:12:28)

Note that when my server starts up everything works fine. Any idea ?

2

There are 2 answers

0
Jay McDoniel On

tsx at its core is powered by esbuild, which does not support Typescript legacy decorators, what Nest uses. Those decorators are important because they supply information to Typescript to emit metadata that Nest needs to resolve the dependencies between classes. If you do want to avoid the build step between writing and running your tests, and don't mind needing a configuration file, you could use @swc/register with the node command and call your tests that way. You'd need an .swcrc file like so:

{
  "jsc": {
    "parser": {
      "syntax": "typescript",
      "decorators": true
    },
    "transform": {
      "legacyDecorator": true,
      "decoratorMetadata": true
    },
    "target": "es2017",
    "keepClassNames": true,
    "baseUrl": "."
  },
  "module": {
    "type": "commonjs",
    "strictMode": true
  }
}

And to install @swc/register and @swc/core, and then you could use node -r @swc/register --test for your command instead of tsx --test and it should all work from there. If you have custom path aliases you'll need a few more options set in the .swcrc file too

0
Charlie Camus On

Thank you for your very helpful answer. I just had to use the non deprecated package @swc-node/register in place of @swc/register

And launch using the command node --import @swc-node/register/esm-register --test', the one that you gave produced compilation errors.