How e2e with guard nestjs

I want to e2e an endpoint called /users with nestjs but I got an error. I have doubts how to make the test pass with a guard.

First error

Nest can't resolve dependencies of the UserModel (?). Please make sure that the argument DatabaseConnection at index [0] is available in the MongooseModule context.

Second error

expected 200 "OK", got 401 "Unauthorized"

App.module

@Module({
  imports: [
    MongooseModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: async (configService: ConfigService) => ({
        uri: configService.mongoUri,
        useNewUrlParser: true,
      }),
      inject: [ConfigService],
    }),
    GlobalModule,
    UsersModule,
    AuthModule,
    PetsModule,
    RestaurantsModule,
    ConfigModule,
  ],
  controllers: [],
  providers: [],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(TokenDataMiddleware)
      .forRoutes({ path: '*', method: RequestMethod.ALL });
  }
}

UsersService

@Injectable()
export class UsersService {
  constructor(
    @InjectModel('User') private readonly userModel: Model<UserDocument>,
    private readonly utilsService: UtilsService,
    private readonly configService: ConfigService,
  ) { }
async getAllUsers(): Promise<UserDocument[]> {
    const users = this.userModel.find().lean().exec();
    return users;
  }
}

Controller

@Controller('users')
export class UsersController {
    constructor(private readonly usersService: UsersService, private readonly utilsService: UtilsService) { }
    @Get()
    @ApiBearerAuth()
    @UseGuards(JwtAuthGuard)
    async users() {
        const users = await this.usersService.getAllUsers();
        return users;
    }

e2e file

describe('UsersController (e2e)', () => {
  let app: INestApplication;
  beforeAll(async () => {
    const testAppModule: TestingModule = await Test.createTestingModule({
      imports: [AppModule, GlobalModule,
        UsersModule,
        AuthModule,
        PetsModule,
        RestaurantsModule,
        ConfigModule],
      providers: [],
    }).compile();

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

  it('GET all users from API', async () => {
    // just mocked users;
    const users = getAllUsersMock.buildList(2);
    const response = await request(app.getHttpServer())
      .get('/users')
      .expect(200);
  });

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

Upvotes: 5

Views: 7244

Answers (1)

Kim Kern
Kim Kern

Reputation: 60347

In a unit test, you test a single unit (service, controller,...) meaning you import one unit and mock all its dependencies. In an e2e test, however, you want to test your whole application so you should import the root module (AppModule) instead of single units or modules. Sometimes you might want to mock particular parts of your application like a database or a 3rd-party API; you can do that with overrideProvider etc.

In your case, you are probably missing the forRoot import of the MongooseModule from your AppModule. Instead of restructuring parts of your application, import the AppModule:

await Test.createTestingModule({
      imports: [AppModule],
    }).compile()
      .overrideProvider(HttpService)
      .useValue(httpServiceMock);

You need to authenticate against your API if it's protected by a guard. You can either create a JWT programatically or use your API for it. I'm assuming you have an endpoint for authentication in the following example:

const loginResponse = await request(app.getHttpServer())
  .post('/auth/login')
  .send({ username: 'user', password: '123456' })
  .expect(201);
// store the jwt token for the next request
const { jwt } = loginResponse.body;

await request(app.getHttpServer())
  .get('/users')
  // use the jwt to authenticate your request
  .set('Authorization', 'Bearer ' + jwt)
  .expect(200)
  .expect(res => expect(res.body.users[0])
    .toMatchObject({ username: 'user' }));

Upvotes: 12

Related Questions