最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Unit and e2e testing with NestJS, TypeORM on Mysql and Passport module - Stack Overflow

programmeradmin2浏览0评论

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 plains 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)
      pile();

    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, posite 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({})
      pile();

    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.

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 plains 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)
      .pile();

    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, posite 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({})
      .pile();

    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.

Share Improve this question edited Aug 29, 2019 at 6:29 Nicola Marella asked Aug 28, 2019 at 14:58 Nicola MarellaNicola Marella 661 silver badge4 bronze badges 3
  • 2 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./a/55366343/4694994 – Kim Kern Commented Aug 28, 2019 at 15:11
  • 1 Please, add more code, add more information – user3945261 Commented Aug 28, 2019 at 16:55
  • I have the same issue with defaultStrategy. Tried to import PassportModule in the test module but it didn't help. – Miron Ophir Commented Mar 1, 2020 at 16:01
Add a ment  | 

1 Answer 1

Reset to default 4

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

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

发布评论

评论列表(0)

  1. 暂无评论