'Nest : test NestMiddleware, can't trigger event

I use a NestMiddleware for HTTP logging (see http-logging-middleware.ts)

This middleware works fine but when I try to test this NestMiddleware with Jest, I don't know how to trigger the event "close" on the response (see http-logging-middleware.spec.ts)

Any help is welcome :-)

Environnement

$ npm --version 
6.14.13

$ node --version
v14.17.0

$ nest --version
8.2.1

http-logging-middleware.ts

import { Injectable, Logger, NestMiddleware } from '@nestjs/common';
import { NextFunction, Request, Response } from 'express';

@Injectable()
export class HttpLoggingMiddleware implements NestMiddleware {
  private logger = new Logger(HttpLoggingMiddleware.name, { timestamp: true });

  use(request: Request, response: Response, next: NextFunction): void {
    const now = Date.now();
    const { baseUrl, ip, method } = request;
    const userAgent = request.headers['user-agent'] || '';
    response.on('close', () => {
      const statusCode = response.statusCode;
      const contentLength = response['_contentLength'];
      const message = {
        statusCode: statusCode,
        method: method,
        path: baseUrl,
        contentLength: contentLength,
        delay: `${Date.now() - now}`,
        ip: ip,
        userAgent: userAgent,
      };
      this.logger.log(message);
    });
    next();
  }
}

http-logging-middleware.spec.ts

import { NextFunction } from 'express';
import { Test } from '@nestjs/testing';
import * as httpMocks from 'node-mocks-http';

import { HttpLoggingMiddleware } from './http-logging-middleware';

describe('HttpLoggingMiddleware', () => {
  let httpLoggingMiddleware: HttpLoggingMiddleware;

  beforeEach(async () => {
    const module = await Test.createTestingModule({
      providers: [HttpLoggingMiddleware],
    }).compile();

    httpLoggingMiddleware = module.get<HttpLoggingMiddleware>(
      HttpLoggingMiddleware,
    );
  });

  afterEach(() => {
    jest.clearAllMocks();
  });

  it('should be defined', () => {
    expect(httpLoggingMiddleware).toBeDefined();
  });

  describe('use', () => {
    it('should intercept request and log related infos', (done: any) => {
      const mockRequest = httpMocks.createRequest();
      const mockResponse = httpMocks.createResponse();
      const nextFunction: NextFunction = jest.fn(() => {
        mockResponse.emit('close');
      });
      httpLoggingMiddleware.use(mockRequest, mockResponse, nextFunction);
      expect(nextFunction).toHaveBeenCalled();
    });
  });
});



Solution 1:[1]

Thanks to [https://github.com/nestjs/nest/blob/master/integration/hello-world/e2e/middleware.spec.ts][1] (Kamil Mysliwiec) the test works :-)

http-logging-middleware.spec.ts #2

import {
  Controller,
  Get,
  INestApplication,
  MiddlewareConsumer,
  Module,
} from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { HttpLoggingMiddleware } from './http-logging-middleware';
import { createMock } from '@golevelup/ts-jest';
import * as request from 'supertest';

import { LoggerModule } from '../../logger/logger.module';
import { Logger } from '../../logger/logger.service';

@Controller()
class TestController {
  @Get('test')
  test() {
    return 'test';
  }
}

const loggerMock = createMock<Logger>();

@Module({
  controllers: [TestController],
  imports: [LoggerModule],
  providers: [
    {
      provide: Logger,
      useValue: loggerMock,
    },
  ],
})
class TestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(HttpLoggingMiddleware).forRoutes('*');
  }
}

describe('HttpLoggingMiddleware', () => {
  let app: INestApplication;

  beforeEach(async () => {
    const module = await Test.createTestingModule({
      imports: [TestModule],
      providers: [
        HttpLoggingMiddleware,
        {
          provide: Logger,
          useValue: loggerMock,
        },
      ],
    }).compile();
    app = module.createNestApplication();
    await app.init();
  });

  it(`should intercept request and log related infos`, () => {
    return request(app.getHttpServer())
      .get('/test')
      .expect(200, 'test')
      .then(() => {
        expect(loggerMock.log).toBeCalledTimes(1);
      });
  });

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


  [1]: https://github.com/nestjs/nest/blob/master/integration/hello-world/e2e/middleware.spec.ts

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Olivier Deslandes