Nest: End to End tests with Passport JWT

Created on 3 Oct 2020  ·  9Comments  ·  Source: nestjs/nest

I am struggling to implement e2e test. Outside test environment in production my application works well with passport, but as soon as I import the main module in my e2e tests, I am getting a strange error from TypeError: passport.use is not a function. I am using Passport JWT Strategy implementation. I cannot seem to get past this


describe('Stand Module', () => {

  let app: INestApplication;

  beforeEach(async () => {

    const moduleRef = await Test.createTestingModule({
      imports: [
        AppModule
      ]

    })
      //.overrideProvider(StandService)
      //.useValue(standServiceMock)
      .compile();


    app = moduleRef.createNestApplication();
    await app.init();

  });

  it('/GET all stands', () => {
    return request(app.getHttpServer())
    .get('/stand')
    .expect(HttpStatus.UNAUTHORIZED);
  });

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

Below is what I am getting but in production everything works just fine.

PASS  src/stand/service/stand.service.spec.ts
 PASS  src/stand/controller/stand.controller.spec.ts
 FAIL  src/stand/stand.e2e.spec.ts (6.304s)
  ● Stand Module › /GET all stands

    TypeError: passport.use is not a function

       9 | export class JwtStrategy extends PassportStrategy(Strategy) {
      10 |   constructor(private readonly authService: AuthService) {
    > 11 |     super({
         |     ^
      12 |       jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      13 |       secretOrKey: AppConfigs.secret,
      14 |     });

@kamilmysliwiec I really appreciate your help

needs triage question 🙌

Most helpful comment

@woodchuck @rungwe Gotcha. Well, by saying to import from '../' that affects all import statements, not just the ones in your application, so when there is an import * as passport from 'passport' call in this file, it finds the first directory called passport which happens to be @nestjs/passport. The exports from here are:

{
      __esModule: true,
      AbstractStrategy: [class AbstractStrategy],
      AuthGuard: [Function (anonymous)],
      AuthModuleOptions: [class AuthModuleOptions],
      PassportModule: [class PassportModule],
      PassportSerializer: [class PassportSerializer],
      PassportStrategy: [Function: PassportStrategy]
    }

Which obviously does not have a use method. Normally, passport's exports look like this:

Authenticator {
      _key: 'passport',
      _strategies: {
        session: SessionStrategy {
          name: 'session',
          _deserializeUser: [Function: bound ]
        }
      },
      _serializers: [],
      _deserializers: [],
      _infoTransformers: [],
      _framework: {
        initialize: [Function: initialize],
        authenticate: [Function: authenticate]
      },
      _userProperty: 'user',
      _sm: SessionManager {
        _key: 'passport',
        _serializeUser: [Function: bound ]
      },
      Authenticator: [Function: Authenticator],
      Passport: [Function: Authenticator],
      Strategy: <ref *1> [Function: Strategy] { Strategy: [Circular *1] },
      strategies: { SessionStrategy: [Function: SessionStrategy] }
    }

To solve this,you can use "<rootDir>/../" instead of ../ and it will only resolve from your rootDir (which is the test directory)

All 9 comments

Please provide a minimum reproduction repository.

I started to work up a minimum reproduction repository and it kept working. I narrowed it down to this moduleDirectories line, which I'd added in my failing version:

```--- a/test/jest-e2e.json
+++ b/test/jest-e2e.json
@@ -1,5 +1,4 @@
{

  • "moduleDirectories": ["node_modules", "../"],
    "moduleFileExtensions": ["js", "json", "ts"],
    "rootDir": ".",
    "testEnvironment": "node",
    ```

Here's a repo that reproduces the problem: https://github.com/woodchuck/nestjs-issue-5522
The first commit is the initial nest-generated app, the second sets up a JwtAuthGuard, and the third has the change that introduces the error.

@woodchuck Why do you change the moduleDirectories property? It works just fine as moduleDirectories: [node_modules].

I'm able to reproduce the problem, but if I use the property above without the ../ then there is no issue with running the code

Hi @jmcdo29. I wanted to reference imports in some areas of the code based on the root dir, rather than using a relative path. For instance, I like using

import { FooModel } from 'src/database/models/foo.model';

vs.

import { FooModel } from '../../database/models/foo.model';

@jmcdo29 If you happen to use absolute imports, the below moduleDirectories config is necessary

-  "moduleDirectories": ["node_modules", "./"],

@woodchuck @rungwe Gotcha. Well, by saying to import from '../' that affects all import statements, not just the ones in your application, so when there is an import * as passport from 'passport' call in this file, it finds the first directory called passport which happens to be @nestjs/passport. The exports from here are:

{
      __esModule: true,
      AbstractStrategy: [class AbstractStrategy],
      AuthGuard: [Function (anonymous)],
      AuthModuleOptions: [class AuthModuleOptions],
      PassportModule: [class PassportModule],
      PassportSerializer: [class PassportSerializer],
      PassportStrategy: [Function: PassportStrategy]
    }

Which obviously does not have a use method. Normally, passport's exports look like this:

Authenticator {
      _key: 'passport',
      _strategies: {
        session: SessionStrategy {
          name: 'session',
          _deserializeUser: [Function: bound ]
        }
      },
      _serializers: [],
      _deserializers: [],
      _infoTransformers: [],
      _framework: {
        initialize: [Function: initialize],
        authenticate: [Function: authenticate]
      },
      _userProperty: 'user',
      _sm: SessionManager {
        _key: 'passport',
        _serializeUser: [Function: bound ]
      },
      Authenticator: [Function: Authenticator],
      Passport: [Function: Authenticator],
      Strategy: <ref *1> [Function: Strategy] { Strategy: [Circular *1] },
      strategies: { SessionStrategy: [Function: SessionStrategy] }
    }

To solve this,you can use "<rootDir>/../" instead of ../ and it will only resolve from your rootDir (which is the test directory)

@jmcdo29 thanks a lot that solved the problem. The solution works perfectly

@jmcdo29 Works for me too. Thanks!

Was this page helpful?
0 / 5 - 0 ratings