简体   繁体   English

如何在 typescript 中使用 sinon 存根 express 中间件?

[英]How to stub express middleware using sinon in typescript?

I'm trying to write an integration test for my express router using typescript, mocha, sinon and chai-http.我正在尝试使用 typescript、mocha、sinon 和 chai-http 为我的快速路由器编写集成测试。 This router uses a custom middleware that I wrote which checks for JWT in the header.该路由器使用我编写的自定义中间件,用于检查 header 中的 JWT。

Ideally, I want to stub my authMiddleware so that I can control its behaviour without actually providing valid/invalid JWT for every test case.理想情况下,我想存根我的authMiddleware以便我可以控制它的行为,而无需为每个测试用例实际提供有效/无效的 JWT。

When I try to stub authMiddleware in my tests, I realised that express app uses the actual implementation of authMiddleware rather than mocked one.当我尝试在我的测试中存根authMiddleware时,我意识到 express app使用authMiddleware的实际实现而不是模拟一个。

I've tried to import app after mocking authMiddleware using dynamic imports of typescript but it didn't work also.我尝试使用 typescript 的动态导入在 mocking authMiddleware之后导入app ,但它也不起作用。

authMiddleware.ts 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主题路由器.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应用程序.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主题测试.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(); }); }); });

When I run the above test I see that mock authMiddleware is not called.当我运行上述测试时,我看到没有调用模拟authMiddleware app in tests uses the real implementation of authMiddleware object.测试中的app使用authMiddleware object 的真实实现。

Is there a way to stub express middleware and pass it to app explicitly?有没有办法存根 express 中间件并将其显式传递给应用程序?

I've just explained what is happening in this response , however giving only workaround.我刚刚解释了此响应中发生的情况,但仅提供了解决方法。

After few thoughts, i believe the best way to overcome this is to remove global state from your modules and capture whole initialization code into explicitly called functions (or classes, if you like), so you can create your server for each tests.经过一番思考,我认为克服这个问题的最佳方法是从模块中删除全局 state 并将整个初始化代码捕获到显式调用的函数(或类,如果你愿意)中,这样你就可以为每个测试创建服务器。 Namely, change your architecture to:即,将您的架构更改为:

// 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());
}

Now, you can create new "app" in each setup callback:现在,您可以在每个设置回调中创建新的“应用程序”:

// 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
});

This approach has many advantages, among others:这种方法有很多优点,其中包括:

  • you can mock different functions for different tests;您可以为不同的测试模拟不同的功能;
  • you're effectively removing "implicit singletons" from code whose existence was root cause of your issue.您正在有效地从代码中删除“隐式单例”,这些代码的存在是问题的根本原因。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM