如何为 JWT 策略编写单元测试用例
Posted
技术标签:
【中文标题】如何为 JWT 策略编写单元测试用例【英文标题】:How to write unit test case for JWT strategy 【发布时间】: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);
,
),
);
【问题讨论】:
【参考方案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 ***/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
【讨论】:
以上是关于如何为 JWT 策略编写单元测试用例的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Kafka Streams 为应用程序编写单元测试用例