DEV Community

Maximiliano Ezequiel Carrizo
Maximiliano Ezequiel Carrizo

Posted on

Write your Nest.js 12 tests as decorated classes with Mocha

Nest.js applications are built from classes, decorators and dependency injection. Tests usually aren't: they are anonymous functions that fetch what they need with app.get(Component).

nestjs-mocha-decorators lets you write tests the same way you write the rest of your app. Suites and tests are plain classes and methods, and test classes are Nest providers.

@TestSuite('Demo Test Suite')
export class DemoTest {
  @Inject()
  private readonly httpService!: HttpService;

  @Test('Get Test')
  async testGet(): Promise<void> {
    const { data } = await firstValueFrom(this.httpService.get('http://localhost:4343/demo'));
    expect(data).toBe('Hello, Test world !');
  }
}
Enter fullscreen mode Exit fullscreen mode

@TestSuite is a Mocha describe, @Test is a Mocha it, and @Inject() works as it does anywhere else in Nest.

Installation

You need Nest.js 12, Mocha 12 and Node.js ^20.19.0 or >=22.12.0. TypeScript 7 does not work with ts-node, so this setup uses TypeScript 6.0.

npm i --save-dev nestjs-mocha-decorators mocha ts-node typescript@~6.0.0 @nestjs/testing @types/mocha @types/node
Enter fullscreen mode Exit fullscreen mode

Your tsconfig.json needs these options:

{
  "compilerOptions": {
    "module": "node20",
    "types": ["node", "mocha"],
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}
Enter fullscreen mode Exit fullscreen mode

Boot the app, then run Mocha

Create test/test.entrypoint.ts. It boots your app, declares the tests and starts Mocha:

import { HttpModule } from '@nestjs/axios';
import { Test } from '@nestjs/testing';
import { run } from 'mocha';
import { MochaTestModule, MochaTestService } from 'nestjs-mocha-decorators';
import { DemoTest } from './tests/demo.test';
import { YourAppModule } from './your-app/your-app.module';

async function bootstrap(): Promise<void> {
  try {
    const testingModule = await Test.createTestingModule({
      imports: [
        YourAppModule, // The app to test
        MochaTestModule.registerTests([DemoTest], [HttpModule]), // Your tests, and the modules they need
      ],
    }).compile();

    const app = testingModule.createNestApplication();
    await app.listen(4343);

    app.get(MochaTestService).declareTests();
    run();
  } catch (error) {
    console.error(error);
    process.exit(1);
  }
}

bootstrap();
Enter fullscreen mode Exit fullscreen mode

Then add the test script to your package.json:

{
  "scripts": {
    "test": "mocha --delay --exit --require ts-node/register test/test.entrypoint.ts"
  }
}
Enter fullscreen mode Exit fullscreen mode

--delay makes Mocha wait until run() is called, once Nest.js has started.

Share tests with inheritance

Because tests are classes, a base class can hold the tests that several suites have in common. Each subclass decorated with @TestSuite runs them, and can override a test method:

abstract class CrudTest {
  @Test('Lists items')
  async list(): Promise<void> {
    // ...
  }
}

@TestSuite('Users')
export class UsersTest extends CrudTest {}

@TestSuite('Orders')
export class OrdersTest extends CrudTest {}
Enter fullscreen mode Exit fullscreen mode

Run code around every test

Hooks live in their own class, which you register like any other test class. They run around every test of every suite:

import { TestEvent, TestEventsEnum, TestEventsSuite } from 'nestjs-mocha-decorators';

@TestEventsSuite()
export class Events {
  @TestEvent(TestEventsEnum.BEFORE_EACH)
  async beforeEach(): Promise<void> {
    // Runs before each test
  }

  @TestEvent(TestEventsEnum.AFTER_EACH)
  async afterEach(): Promise<void> {
    // Runs after each test
  }
}
Enter fullscreen mode Exit fullscreen mode

Run a single suite or test

Pick one with an environment variable. Names must match exactly:

SUITETORUN="Demo Test Suite" npm test
TESTTORUN="Get Test" npm test
Enter fullscreen mode Exit fullscreen mode

Try it

The library is MIT licensed. You can find it on npm and GitHub, where issues and suggestions are welcome.

Top comments (0)