【问题标题】:How to write unit test case for JWT strategy如何为 JWT 策略编写单元测试用例
【发布时间】:2020-09-19 09:05:55
【问题描述】:

我是新来的 passport.js,并试图涵盖我的 JWT 策略的单元测试用例。任何人都可以建议如何做到这一点?

// Setup JWT strategy for all requests
passport.use(
  new JWTStrategy(
    {
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: JWT_PRIVATE_KEY,
    },
    async (jwtPayload: any, done: any) => {
      const isUser = jwtPayload.type === EntityType.User;
      const model = isUser ? userModel : vendorModel;
      try {
        const document = await model.findOne({ _id: jwtPayload.id });
        if (document) {
          return done(null, jwtPayload);
        } else {
          return done(null, false);
        }
      } catch (err) {
        return done(err, false);
      }
    },
  ),
);

【问题讨论】:

    标签: unit-testing jestjs passport.js passport-local passport-jwt


    【解决方案1】:

    使用supertest验证全周期

    
    import request from 'supertest';
    import express from 'express';
    import jwt from 'jsonwebtoken'
    
    
    export const createAuthToken = (userId) => {
      const body = { 
        type: EntityType.User,
        id: userId,
      };
      return jwt.sign(body, JWT_PRIVATE_KEY);
    };
    
    // this function should configure express app
    const appLoader = async app => {
      (await import('../app/loaders/express')).expressLoader({ app }); // express bindings and routes
      await import('./'); // passport config
    }
    
    describe('passport-jwt auth', () => {
      const app = express();
      const token = createAuthToken('user1')
    
    
      beforeAll(async () => {
        await appLoader({ app });
      });
    
      it('should verify auth', async () => {
        jest.spyOn(userModel, 'findOne').mockResolvedValueOnce('mocked user document');
    
    
        await request(app)
          .get('/protected-endpoint')
          .set('Authorization', `Bearer ${token}`)
          .expect(200);
      });
    
      it('should verify auth - failure', async () => {
        await request(app)
          .get('/protected-endpoint')
          .set('Authorization', `Bearer wrong-token`)
          .expect(401);
      });
    });
    

    【讨论】:

      【解决方案2】:

      单元测试解决方案:

      index.ts:

      import passport from 'passport';
      import { Strategy as JWTStrategy, ExtractJwt } from 'passport-jwt';
      import { userModel, vendorModel, EntityType } from './models';
      
      const JWT_PRIVATE_KEY = 'secret 123';
      
      passport.use(
        new JWTStrategy(
          {
            jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
            secretOrKey: JWT_PRIVATE_KEY,
          },
          async (jwtPayload: any, done: any) => {
            console.log('123123');
            const isUser = jwtPayload.type === EntityType.User;
            const model = isUser ? userModel : vendorModel;
            try {
              const document = await model.findOne({ _id: jwtPayload.id });
              if (document) {
                return done(null, jwtPayload);
              } else {
                return done(null, false);
              }
            } catch (err) {
              return done(err, false);
            }
          },
        ),
      );
      

      models.ts:

      export enum EntityType {
        User = 'User',
      }
      export const userModel = {
        async findOne(opts) {
          return 'real user document';
        },
      };
      export const vendorModel = {
        async findOne(opts) {
          return 'real vendor document';
        },
      };
      

      index.test.ts:

      import { Strategy as JWTStrategy, ExtractJwt, VerifyCallback, StrategyOptions } from 'passport-jwt';
      import passport from 'passport';
      import { userModel, vendorModel } from './models';
      
      jest.mock('passport-jwt', () => {
        const mJWTStrategy = jest.fn();
        const mExtractJwt = {
          fromAuthHeaderAsBearerToken: jest.fn(),
        };
        return { Strategy: mJWTStrategy, ExtractJwt: mExtractJwt };
      });
      jest.mock('passport', () => {
        return { use: jest.fn() };
      });
      
      describe('62125872', () => {
        let verifyRef;
        beforeEach(() => {
          const mJwtFromRequestFunction = jest.fn();
          (ExtractJwt.fromAuthHeaderAsBearerToken as jest.MockedFunction<
            typeof ExtractJwt.fromAuthHeaderAsBearerToken
          >).mockReturnValueOnce(mJwtFromRequestFunction);
      
          (JWTStrategy as jest.MockedClass<any>).mockImplementation((opt: StrategyOptions, verify: VerifyCallback) => {
            verifyRef = verify;
          });
        });
      
        it('should verify using user model and call done with jwtpayload if user document exists', async () => {
          const payload = { type: 'User', id: 1 };
          const mDone = jest.fn();
      
          jest.spyOn(userModel, 'findOne').mockResolvedValueOnce('mocked user document');
          await import('./');
          await verifyRef(payload, mDone);
          expect(passport.use).toBeCalledWith(expect.any(Object));
          expect(JWTStrategy).toBeCalledWith(
            { jwtFromRequest: expect.any(Function), secretOrKey: 'secret 123' },
            expect.any(Function),
          );
          expect(ExtractJwt.fromAuthHeaderAsBearerToken).toBeCalledTimes(1);
          expect(userModel.findOne).toBeCalledWith({ _id: 1 });
          expect(mDone).toBeCalledWith(null, { type: 'User', id: 1 });
        });
      
        it("should verify using user model and call done with false if user document doesn't exist", async () => {
          const payload = { type: 'User', id: 1 };
          const mDone = jest.fn();
      
          jest.spyOn(userModel, 'findOne').mockResolvedValueOnce('');
          await import('./');
          await verifyRef(payload, mDone);
          expect(passport.use).toBeCalledWith(expect.any(Object));
          expect(JWTStrategy).toBeCalledWith(
            { jwtFromRequest: expect.any(Function), secretOrKey: 'secret 123' },
            expect.any(Function),
          );
          expect(ExtractJwt.fromAuthHeaderAsBearerToken).toBeCalledTimes(1);
          expect(userModel.findOne).toBeCalledWith({ _id: 1 });
          expect(mDone).toBeCalledWith(null, false);
        });
      
        // you can do the rest parts
      });
      

      单元测试结果:

       PASS  stackoverflow/62125872/index.test.ts
        62125872
          ✓ should verify using user model and call done with jwtpayload if user document exists (11ms)
          ✓ should verify using user model and call done with false if user document doesn't exist (2ms)
      
      -----------|---------|----------|---------|---------|-------------------
      File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
      -----------|---------|----------|---------|---------|-------------------
      All files  |      85 |    83.33 |      60 |   84.21 |                   
       index.ts  |   92.86 |       75 |     100 |   92.31 | 24                
       models.ts |   66.67 |      100 |   33.33 |   66.67 | 6,11              
      -----------|---------|----------|---------|---------|-------------------
      Test Suites: 1 passed, 1 total
      Tests:       2 passed, 2 total
      Snapshots:   0 total
      Time:        3.716s, estimated 10s
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-04-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-02-22
        相关资源
        最近更新 更多