1
votes

I'm currently taking over a backend written with NestJS, typeorm on Mysql and passport package for authentication. I'm currently trying to write both unit and e2e tests according to the NestJS documentation but I can't really make it work.

I've tried both importing the root AppModule or a single module. In the first case, it complains that it cannot connect to the database. I've overridden the configs provider for that, made it work on a testing Sqlite database, still passport module will raise an error saying that cannot access property challenge of undefined.

So, let's start with e2e testing, here is my app module:


  imports: [
    ConfigModule,
    CoreModule,
    Logger,
    AuthModule,
    TypeOrmModule.forRootAsync({
      imports: [TypeOrmConfigService],
      useClass: TypeOrmConfigService,
    }),
    UsersModule,
    CoreModule,
    DataModule,
    InitializationModule,
    TokensModule,
    ProjectsModule,
    ReportsModule,
    AccountsModule,
    NotificationModule,
    FaqModule,
    StrategicModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

and here is my e2e-spec file in which i override the config service to setup a temporary database:

class MockTypeOrmConfigService implements TypeOrmOptionsFactory {
  createTypeOrmOptions(): TypeOrmModuleOptions {
    return {
      type: 'sqlite' as any,
      database: ':memory:',
      synchronize: true,
      dropSchema: true,
      entities: [`../src/**/*.entity{.ts,.js}`],
    };
  }
}
const mockTypeOrmConfigService = new MockTypeOrmConfigService();

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

  beforeAll(async () => {
    const moduleFixture = await Test.createTestingModule({
      imports: [AppModule],
    })
      .overrideProvider(TypeOrmConfigService)
      .useValue(mockTypeOrmConfigService)
      .compile();

    app = moduleFixture.createNestApplication();
    await app.init();
  });

  it('/faq (GET)', () => {
    return request(app.getHttpServer())
      .get('/faq')
      .expect(200)
      .expect('Hello World!');
  });
});

this would actually work if only a big part of my schema definition is based on mysql fields (for example json, composite autoincrement and blobs). So my next tentative would be to use single controller modules to create the app and run e2e tests there. Let's say I have this controller:

@Controller('faq')
@UseGuards(AuthGuard())
@ApiBearerAuth()
export class FaqController {
  constructor(private readonly faqService: FaqService) {}

  @Get()
  @ApiOkResponse({ type: FaqEntryDto, isArray: true })
  async getAll(): Promise<FaqEntryDto[]> {
    return this.faqService.findAll();
  }

  @Post()
  @UseGuards(new RolesGuard(UserRole.Admin))
  async create(@Body() body: FaqEntryDto) {
    return this.faqService.create(body);
  }

  @Put('/:id')
  @UseGuards(new RolesGuard(UserRole.Admin))
  async update(
    @Param('id', new ParseIntPipe()) id: number,
    @Body() body: FaqEntryDto
  ) {
    return this.faqService.update(id, body);
  }

  @Delete('/:id')
  @UseGuards(new RolesGuard(UserRole.Admin))
  async delete(@Param('id', new ParseIntPipe()) id: number) {
    return this.faqService.delete(id);
  }
}

and this my test (in which I also override the repository provider:

ntroller (e2e)', () => {
  let app: INestApplication;

  beforeAll(async () => {
    const moduleFixture = await Test.createTestingModule({
      imports: [FaqModule],
      providers: [FaqService],
    })
      .overrideProvider(getRepositoryToken(FaqEntity))
      .useValue({})
      .compile();

    app = moduleFixture.createNestApplication();
    await app.init();
  });

  it('/ (GET)', () => {
    return request(app.getHttpServer())
      .get('/faq')
      .expect(200)
      .expect('Hello World!');
  });
});

then, I would always get messages from the passport auth module, for instance:

[Nest] 4976   - 8/29/2019, 8:27:00 AM   [ExceptionHandler] In order to use "defaultStrategy", please, ensure to import PassportModule in each place where AuthGuard() is being used. Otherwise, passport won't work correctly.
[Nest] 4976   - 8/29/2019, 8:27:00 AM   [ExceptionHandler] Cannot read property 'challenge' of undefined +44ms
TypeError: Cannot read property 'challenge' of undefined

I also tried with overrideGuard on AuthGuard but the result is exactly the same.

Additionally I would like to use typeorm-fixture package but also on this front I'm having big trouble.

1
I feel you, testing with nest.js is a big topic. ;-) However, can you limit your question to a specific problem ideally by adding a relevant code snippet, maybe one thread for each problem? "How does nest.js unit and e2e testing work in general" is a really big topic and cannot be answered in one thread. As a starter, have a look at this thread explaining how to use mocks in nest.js unit tests: stackoverflow.com/a/55366343/4694994Kim Kern
Please, add more code, add more informationAndrey Jakovenko
I have the same issue with defaultStrategy. Tried to import PassportModule in the test module but it didn't help.Miron Ophir

1 Answers

4
votes

Add 'jwt' in your @UseGuards(AuthGuard()) decorator. Otherwise you'll get that error.

It should be like this: @UseGuards(AuthGuard('jwt'))