使用 Jest 的简单模拟护照功能
Simple Mock Function of Passport using Jest
我目前正在对我的所有路由进行单元测试,包括一些使用自定义护照身份验证功能的路由。我正在尝试模拟护照功能以测试错误处理,但我不断收到错误消息:
TypeError: _passport.default.authenticate(...) is not a function
这是在 /controllers/users.js
中运行的实际代码:
export const persistentLogin = (req, res, next) => {
// Authenicate the cookie sent on the req object.
passport.authenticate('jwt', { session: false }, async (authErr, user) => {
// If there is an system error, send 500 error
if (authErr) return res.sendStatus(500);
// If no user is returned, send response showing failure.
if (!user) {
return res.status(200).json({
success: 'false',
});
}
})(req, res, next);
};
这里是/tests/controllers/users.js
中的测试代码:
import passport from 'passport';
import { persistentLogin } from '../../controllers/users';
beforeEach(() => {
mockResponse = () => {
const response = {};
response.status = jest.fn().mockReturnValue(response);
response.json = jest.fn().mockReturnValue(response);
response.sendStatus = jest.fn().mockReturnValue(response);
response.clearCookie = jest.fn().mockReturnValue(response);
response.cookie = jest.fn().mockReturnValue(response);
return response;
};
});
/**
* persistentLogin Tests
*/
describe('Persistent Login Controller', () => {
beforeEach(() => {
req = {};
res = mockResponse();
validateLoginForm.mockClear();
bcrypt.compare.mockClear();
});
// Passport authenication error
test('Should show passport authenication error', async () => {
passport.authenticate = jest.fn((authType, options, callback) => callback('This is an error', null));
await persistentLogin(req, res);
expect(passport.authenticate).toHaveBeenCalledTimes(1);
expect(res.sendStatus).toHaveBeenCalledWith(500);
});
});
如果我不得不猜测,我会说这与 (req, res, next) 对象在事后如何传递到实时函数有关。但由于我们只是模拟函数,我不确定它是否真的需要访问这些对象。
编辑#1:
根据@jakemingolla 的评论,我现在认为这可能是因为 Jest 不是 运行 我的 app.js 文件,它定义了我的自定义 JWT 策略。
这是 /app.js
文件中的代码:
import passport from 'passport';
import passportJWTStrategy from './utils/auth/passport';
app.use(passport.initialize());
passportJWTStrategy(passport);
来自 /utils/auth/passport.js
文件的代码:
import { Strategy } from 'passport-jwt';
/**
* Verifies JWT payload
*
* @param passport The instance of passport module.
*/
export default (passport) => {
const JWTStrategy = Strategy;
// Setup Options Object
const opts = {};
opts.jwtFromRequest = req => req.cookies.jwt;
opts.secretOrKey = process.env.PASSPORT_SECRET;
passport.use(
new JWTStrategy(opts, (jwtPayload, done) => {
if (Date.now() > jwtPayload.expire_date) {
return done('jwt expired');
}
return done(null, jwtPayload);
}),
);
};
您只需要一点零钱:
你对 passport.authenticate
的模拟只需要 return 一个 函数 :
passport.authenticate = jest.fn((authType, options, callback) => () => { callback('This is an error', null); });
在你模拟的问题中 passport.authenticate
,但在这种情况下,你的策略的 verify
函数没有被调用。如果你也想 运行 这个函数或者模拟特定的策略,那么试试这样的事情:
sinon
.stub(passport._strategies.google, 'authenticate')
.callsFake(function verified() {
const self = this;
this._verify(
null,
null,
{
_json: { email: faker.internet.email() },
name: {
givenName: faker.name.firstName(),
familyName: faker.name.lastName(),
},
},
(err, user, info) => {
if (err) {
return self.error(err);
}
if (!user) {
return self.fail(info);
}
return self.success(user, info);
}
);
});
const response = await supertest(app)
.get('/google/callback?code=123');
我目前正在对我的所有路由进行单元测试,包括一些使用自定义护照身份验证功能的路由。我正在尝试模拟护照功能以测试错误处理,但我不断收到错误消息:
TypeError: _passport.default.authenticate(...) is not a function
这是在 /controllers/users.js
中运行的实际代码:
export const persistentLogin = (req, res, next) => {
// Authenicate the cookie sent on the req object.
passport.authenticate('jwt', { session: false }, async (authErr, user) => {
// If there is an system error, send 500 error
if (authErr) return res.sendStatus(500);
// If no user is returned, send response showing failure.
if (!user) {
return res.status(200).json({
success: 'false',
});
}
})(req, res, next);
};
这里是/tests/controllers/users.js
中的测试代码:
import passport from 'passport';
import { persistentLogin } from '../../controllers/users';
beforeEach(() => {
mockResponse = () => {
const response = {};
response.status = jest.fn().mockReturnValue(response);
response.json = jest.fn().mockReturnValue(response);
response.sendStatus = jest.fn().mockReturnValue(response);
response.clearCookie = jest.fn().mockReturnValue(response);
response.cookie = jest.fn().mockReturnValue(response);
return response;
};
});
/**
* persistentLogin Tests
*/
describe('Persistent Login Controller', () => {
beforeEach(() => {
req = {};
res = mockResponse();
validateLoginForm.mockClear();
bcrypt.compare.mockClear();
});
// Passport authenication error
test('Should show passport authenication error', async () => {
passport.authenticate = jest.fn((authType, options, callback) => callback('This is an error', null));
await persistentLogin(req, res);
expect(passport.authenticate).toHaveBeenCalledTimes(1);
expect(res.sendStatus).toHaveBeenCalledWith(500);
});
});
如果我不得不猜测,我会说这与 (req, res, next) 对象在事后如何传递到实时函数有关。但由于我们只是模拟函数,我不确定它是否真的需要访问这些对象。
编辑#1:
根据@jakemingolla 的评论,我现在认为这可能是因为 Jest 不是 运行 我的 app.js 文件,它定义了我的自定义 JWT 策略。
这是 /app.js
文件中的代码:
import passport from 'passport';
import passportJWTStrategy from './utils/auth/passport';
app.use(passport.initialize());
passportJWTStrategy(passport);
来自 /utils/auth/passport.js
文件的代码:
import { Strategy } from 'passport-jwt';
/**
* Verifies JWT payload
*
* @param passport The instance of passport module.
*/
export default (passport) => {
const JWTStrategy = Strategy;
// Setup Options Object
const opts = {};
opts.jwtFromRequest = req => req.cookies.jwt;
opts.secretOrKey = process.env.PASSPORT_SECRET;
passport.use(
new JWTStrategy(opts, (jwtPayload, done) => {
if (Date.now() > jwtPayload.expire_date) {
return done('jwt expired');
}
return done(null, jwtPayload);
}),
);
};
您只需要一点零钱:
你对 passport.authenticate
的模拟只需要 return 一个 函数 :
passport.authenticate = jest.fn((authType, options, callback) => () => { callback('This is an error', null); });
在你模拟的问题中 passport.authenticate
,但在这种情况下,你的策略的 verify
函数没有被调用。如果你也想 运行 这个函数或者模拟特定的策略,那么试试这样的事情:
sinon
.stub(passport._strategies.google, 'authenticate')
.callsFake(function verified() {
const self = this;
this._verify(
null,
null,
{
_json: { email: faker.internet.email() },
name: {
givenName: faker.name.firstName(),
familyName: faker.name.lastName(),
},
},
(err, user, info) => {
if (err) {
return self.error(err);
}
if (!user) {
return self.fail(info);
}
return self.success(user, info);
}
);
});
const response = await supertest(app)
.get('/google/callback?code=123');