我正在尝试使用 typescript、mocha、sinon 和 chai-http 为我的 Express 路由器编写集成测试。该路由器使用我编写的自定义中间件来检查 header 中的 JWT。
理想情况下,我想 stub 我的 authMiddleware
,以便我可以控制其行为,而无需为每个测试用例实际提供有效/无效的 JWT。
当我尝试在测试中 stub authMiddleware
时,我意识到 Express app
使用 authMiddleware
的实际实现,而不是模拟的实现。
我尝试使用动态导入 typescript 来模拟 authMiddleware
后导入 app
,但它也不起作用。
authMiddleware.ts
import { Request, Response, NextFunction } from 'express';
export default class AuthMiddleware {
verifyToken(req: Request, res: Response, next: NextFunction) :void {
console.log('Actual implemetation of verifyToken is called!');
// verify token
next();
}
}
subjectRouter.ts
import express from'express';
import AuthMiddleware from '../middleware/authMiddleware';
import * as subjectController from '../controller/subjectController';
const router = express.Router();
const authMiddleware = new AuthMiddleware();
router.post('/', authMiddleware.verifyToken, subjectController.createSubject);
export default router;
app.ts
import express from 'express';
import subjectRoute from './route/subjectRoute';
// Initilize express app
const app = express();
app.set("port", 3000);
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
// Routers
app.use('/user', userRoute);
app.use('/subject', subjectRoute);
export default app;
subjectTests.ts
import app from '../../src/app';
import AuthMiddleware from '../../../src/middleware/AuthMiddleware';
describe('Subject', () => {
let app;
beforeEach(async () => {
sinon.stub(AuthMiddleware.prototype, 'verifyToken').callsFake((req: Request, res: Response, next: NextFunction): void => {
console.log('Fake verifyToken is called!');
// THIS IS NEVER CALLED IN TESTS...
});
app = (await import('../../../src/app')).default;
});
it('should throw 403 when jwt is missing in header', (done) => {
request(app)
.post(/subject)
.end((err, res) => {
expect(res).has.status(403);
done();
});
});
});
当我运行上述测试时,我发现模拟 authMiddleware
未被调用。测试中的 app
使用 authMiddleware
对象的真实实现。
有没有办法 stub 快速中间件并将其显式传递给应用程序?
最佳答案
我刚刚解释了 this response 中发生的情况,但是仅给出解决方法。
经过一番思考,我相信克服这个问题的最佳方法是从模块中删除全局状态并将整个初始化代码捕获到显式调用的函数(或类,如果您愿意)中,这样您就可以为每个测试创建服务器。即,将您的架构更改为:
// router.ts
export function createRouter() {
/// ...
router.post('/', authMiddleware.verifyToken, subjectController.createSubject);
return router;
}
// app.ts
import { createRouter} from "./router.js"
export function createApp() {
///same code as currently, but in function
app.use('/subject', createRouter());
}
现在,您可以在每个设置回调中创建新的“应用程序”:
// test.ts
beforeEach(async () => {
sinon.stub(AuthMiddleware.prototype, 'verifyToken').callsFake((req: Request, res: Response, next: NextFunction): void => {
...
});
app = (await import('../../../src/app')).createApp(); // note that we create new app for each test, so you're not polutting global state of app
});
这种方法有很多优点,其中包括:
- 您可以针对不同的测试模拟不同的函数;
- 您正在有效地从代码中删除“隐式单例” 存在是您问题的根本原因。
关于typescript - 如何在 typescript 中使用 sinon 来 stub Express 中间件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62604398/