2

我正在尝试使用 typescript、mocha、sinon 和 chai-http 为我的 express 路由器编写一个集成测试。该路由器使用我编写的自定义中间件,用于检查标头中的 JWT。

理想情况下,我想存根 myauthMiddleware以便我可以控制其行为,而无需为每个测试用例实际提供有效/无效的 JWT。

当我尝试authMiddleware在我的测试中存根时,我意识到 expressapp使用的是实际实现authMiddleware而不是模拟一个。

我尝试在使用打字稿的动态导入进行模拟appauthMiddleware导入,但它也不起作用。

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

主题路由器.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;

应用程序.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;

主题测试.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没有调用 mock。app在测试中使用对象的真实实现authMiddleware

有没有办法存根 express 中间件并将其显式传递给应用程序?

4

1 回答 1

2

我刚刚解释了此响应中发生的情况,但仅提供了解决方法。

经过一番思考,我相信克服这个问题的最佳方法是从模块中删除全局状态并将整个初始化代码捕获到显式调用的函数(或类,如果你喜欢)中,这样你就可以为每个测试创建服务器。即,将您的架构更改为:

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

这种方法有很多优点,其中包括:

  • 您可以为不同的测试模拟不同的功能;
  • 您正在有效地从代码中删除“隐式单例”,这些代码的存在是问题的根本原因。
于 2020-07-01T18:07:08.093 回答