如何在打字稿中使用sinon存根表达中间件? [英] How to stub express middleware using sinon in typescript?

查看:93
本文介绍了如何在打字稿中使用sinon存根表达中间件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用Typescript,mocha,sinon和chai-http为我的快速路由器编写一个集成测试.该路由器使用我编写的自定义中间件,该中间件检查头中的JWT.

I'm trying to write an integration test for my express router using typescript, mocha, sinon and chai-http. This router uses a custom middleware that I wrote which checks for JWT in the header.

理想情况下,我想对我的authMiddleware存根,以便可以控制其行为,而无需为每个测试用例实际提供有效/无效的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进行存根时,我意识到express app使用的是authMiddleware的实际实现,而不是模拟的.

When I try to stub authMiddleware in my tests, I realised that express app uses the actual implementation of authMiddleware rather than mocked one.

我尝试使用动态导入的Typescript模拟authMiddleware之后导入app,但是它也无法正常工作.

I've tried to import app after mocking authMiddleware using dynamic imports of typescript but it didn't work also.

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对象的实际实现.

When I run the above test I see that mock authMiddleware is not called. app in tests uses the real implementation of authMiddleware object.

是否有一种方法可以对表示中间件进行存根并将其显式传递给应用程序?

Is there a way to stub express middleware and pass it to app explicitly?

推荐答案

我刚刚解释了

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. 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.

这篇关于如何在打字稿中使用sinon存根表达中间件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆