nestjs / nest

A progressive Node.js framework for building efficient, scalable, and enterprise-grade server-side applications with TypeScript/JavaScript 🚀
https://nestjs.com
MIT License
67.69k stars 7.63k forks source link

E2E Test: server.close is not a function #295

Closed hpop closed 6 years ago

hpop commented 6 years ago

I'm submitting a...


[ ] Regression 
[x] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.

Current behavior

I wrote an e2e test for my application. Where I create my Nest module in the before method and close the module in after.

describe('Application e2e', () => {
  const server = express();
  let app: INestApplication;

  before(async () => {
    const module = await Test.createTestingModule({
      modules: [ApplicationModule],
    })
      .compile();
    app = module.createNestApplication(server);
    await app.init();
  });

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

 // tests...

This works fine but as soon as I add a WebSocketGateway the the module, I get the following error:

TypeError: server.close is not a function
      at servers.forEach (node_modules/@nestjs/websockets/socket-module.js:42:48)
      at Map.forEach (native)
      at SocketModule.close (node_modules/@nestjs/websockets/socket-module.js:42:17)
      at NestApplication.close (node_modules/@nestjs/core/nest-application.js:118:48)
      at Context.after (src/modules/tournament/tests/teamorder.e2e-spec.ts:35:15)
      at <anonymous>
      at process._tickCallback (internal/process/next_tick.js:188:7)

Expected behavior

The module should close successfully.

Minimal reproduction of the problem with instructions

It seems like an easy to fix error but I will create an example if needed.

Environment


Nest version: 4.4.2


For Tooling issues:
- Node version: 8.6.0  
- Platform:  Mac 

Others:

Thanks for this very great project!

kamilmysliwiec commented 6 years ago

Hi @hpop, I can't reproduce this issue. Could you share more code?

hpop commented 6 years ago

I am a bit busy at the moment but I will send you some code till the end of the week.

wbhob commented 6 years ago

And no code was ever sent...

joshdawson commented 6 years ago

I am seeing a very similar issue.

"before all" hook:
     TypeError: instance.onModuleInit is not a function
      at iterare_1.default.map.filter.filter.forEach.instance (node_modules\@nestjs\core\nest-application.js:206:43)
      at IteratorWithOperators.forEach (node_modules\iterare\src\iterate.ts:174:13)
      at NestApplication.callModuleInitHook (node_modules\@nestjs\core\nest-application.js:206:14)
      at modules.forEach.module (node_modules\@nestjs\core\nest-application.js:197:18)
      at Map.forEach (<anonymous>)
      at NestApplication.callInitHook (node_modules\@nestjs\core\nest-application.js:196:17)
      at NestApplication.<anonymous> (node_modules\@nestjs\core\nest-application.js:78:18)
      at Generator.next (<anonymous>)
      at fulfilled (node_modules\@nestjs\core\nest-application.js:4:58)
      at <anonymous>

and

       "after all" hook:
     TypeError: instance.onModuleDestroy is not a function
      at iterare_1.default.map.filter.filter.forEach.instance (node_modules\@nestjs\core\nest-application.js:223:43)
      at IteratorWithOperators.forEach (node_modules\iterare\src\iterate.ts:174:13)
      at NestApplication.callModuleDestroyHook (node_modules\@nestjs\core\nest-application.js:223:14)
      at modules.forEach.module (node_modules\@nestjs\core\nest-application.js:214:18)
      at Map.forEach (<anonymous>)
      at NestApplication.callDestroyHook (node_modules\@nestjs\core\nest-application.js:213:17)
      at NestApplication.close (node_modules\@nestjs\core\nest-application.js:164:14)
      at Object.<anonymous> (src\room\rooms.controller.spec.ts:90:15)
      at Generator.next (<anonymous>)
      at src\room\rooms.controller.spec.ts:7:71
      at new Promise (<anonymous>)
      at __awaiter (src\room\rooms.controller.spec.ts:3:12)
      at Context.after (src\room\rooms.controller.spec.ts:89:20)

My nest core version is 4.6.0 and my nest testing version is 4.6.6.

describe.only('e2e tests', () => {
  let server;
  let app: INestApplication;
  let mockRoomService: RoomService;

  before(async () => {
    mockRoomService = mock(RoomService);
    when(mockRoomService.findAll()).thenReturn(Promise.resolve([] as any));

    const module = await Test.createTestingModule({
      components: [RoomService],
      controllers: [RoomController, RoomsController],
    })
    .overrideComponent(RoomService)
    .useValue(instance(mockRoomService))
    .compile();

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

  it('/GET', (done) => {
    request(server)
      .get('/')
      .then((response) => {
        expect(response).to.equal([]);
        done();
      })
      .catch(done);
  });

  after(async () => {
    await app.close();
  });
});
ghost commented 6 years ago

@kamilmysliwiec, I'm seeing the exact same issue as the OP. E2E tests that create an application from a module that uses a WebSocketGateway throw an error on app.close().

core.module.ts

import { Module } from '@nestjs/common';
import { DeviceGateway } from './device.gateway';

@Module({
    components: [DeviceGateway]
})
export class CoreModule {}

device.gateway.ts

of note here: of all the lifecycle methods implemented, only afterInit is executed

import {
    OnGatewayConnection, OnGatewayDisconnect,
    OnGatewayInit,
    SubscribeMessage,
    WebSocketGateway,
    WebSocketServer,
    WsResponse
} from '@nestjs/websockets';
import * as config from 'config';

// App listens on 9000, WS listens on 9001
@WebSocketGateway({ port: config.Ws.port, namespace: 'ws/devices' })
export class DeviceGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
{
    // I am executed
    afterInit(server:any)
    {
        console.log('After Init');
    }

    // I am never executed
    handleConnection(client:any)
    {
        console.log('On Connection');
    }

    // I am never executed
    handleDisconnect(client:any)
    {
        console.log('On Disconnect');
    }
}

iambroken.e2e.ts

import { INestApplication } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { CoreModule } from '../../../src/core/core.module';
import * as config from 'config';
import * as SocketIOClient from 'socket.io-client';
import Socket = SocketIOClient.Socket;

describe('IAmBroken (e2e)', () =>
{
    let app:INestApplication,
    module:TestingModule,
        socket:any;

    before(async () =>
    {
        module = await Test.createTestingModule({
            imports: [CoreModule]
    }).compile();

    app = module.createNestApplication();
    await app.init();
        socket = SocketIOClient.connect(`ws://localhost:${config.Ws.port}`, { forceNew: true });
    });

    after(function(done)
    {
        // I cause BIG problems
        app.close();
        done();
    });

    it('connects and disconnects without error', () =>
    {
        socket.on('connect', () =>
        {
            expect(socket.connected).to.eq(true);

            socket.on('disconnect', (s:string) =>
            {
                expect(socket.connected).to.eq(false);
                expect(s).to.eq('io client disconnect');
            });

            socket.close();
        });
    });
});

The above test fails as long as the DeviceGateway is included in the CoreModule. This actually means that all tests are failing as long as the DeviceGateway is included. Here is the stack:

2) IAmBroken (e2e)
       "after all" hook:
     TypeError: server.close is not a function
      at servers.forEach (node_modules/@nestjs/websockets/socket-module.js:42:48)
      at Map.forEach (<anonymous>)
      at SocketModule.close (node_modules/@nestjs/websockets/socket-module.js:42:17)
      at NestApplication.close (node_modules/@nestjs/core/nest-application.js:176:48)
      at Context.<anonymous> (e2e/core/jobs/iambroken.e2e.ts:26:7)

If I get rid of app.close() in the after all hook above, the test passes, but as I mentioned, only the afterInit lifecycle hook is ever executed. handleDisconnect and handleConnection never execute.

I would venture that I'm missing something important in your websockets package, but I can't find an answer in the docs. I'd really appreciate some guidance here.

Update

On closer inspection, I don't think it's me. If I don't use a namespace in my WebSocketGateway, everything works as expected. If I do, when nest calls SocketModule.close there is no server with my namespace, hence the above error.

lock[bot] commented 5 years ago

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.