使用Mocha+Supertest做接口测试时首次运行正常、后续token未定义的问题分析与解决
我最近在用Mocha和Supertest做Node.js接口自动化测试时碰到了一个棘手的问题:第一次运行所有测试用例完全正常,JWT Token能顺利获取,所有测试都通过,但第二次执行测试时,Token直接变成了undefined,紧接着就抛出了"jwt malformed"的错误。
最开始我觉得自己的方案没问题——毕竟在其他编程语言的测试场景里,我都是用类似的方式在测试前获取一次认证Token然后复用。但在Mocha的环境里,这个逻辑却出了问题。
初始的测试代码(有问题版本)
我的核心测试逻辑是在before钩子中先初始化测试数据,再调用登录接口拿到Token,之后所有需要认证的接口都用这个Token:
const supertest = require('supertest'); const app = require('../app'); const server = supertest.agent(app); let token = ''; before(async () => { addDummyObjects(); await server.post('/user/login') .type('json') .send({ "username": "testUser1", "email": "a@c.com", "password": "123456" }) .then(res => token = res.body.token); }); // 各个测试用例组... after(async () => { await dropAllCollections(); mongoose.connection.close(); });
问题排查与原因分析
折腾了一阵后我才反应过来:before钩子只会在整个测试套件执行前运行一次。第一次测试时,before里的流程没问题,能拿到Token;但after钩子中我清空了所有数据库集合还关闭了连接。第二次执行测试时,before重新运行,但addDummyObjects()可能因为数据库连接的初始化时序问题,没有正确创建出测试用户,导致登录接口返回的响应里根本没有Token字段,自然token就变成了undefined。
简单来说,就是重复执行时,单次初始化的Token依赖的测试数据已经被清理,而重新初始化的流程又没跟上,导致Token失效。
解决后的代码方案
我调整了钩子的分工:
- 保留
before钩子,专门负责初始化测试数据(确保只执行一次); - 新增
beforeEach钩子,把获取Token的登录请求移到这里——这样每个测试用例执行前都会重新登录获取一次有效的Token,彻底避免了重复执行时Token失效的问题。
修改后的核心代码如下:
let token = ''; before(async () => { await addDummyObjects(); // 加上await确保异步数据初始化完成 }); beforeEach(async () => { // 每个测试用例前重新获取Token const res = await server.post('/user/login') .type('json') .send({ "username": "testUser1", "email": "a@c.com", "password": "123456" }); token = res.body.token; }); // 各个测试用例组... after(async () => { await dropAllCollections(); await mongoose.connection.close(); // 加上await确保连接正常关闭 });
调整后不管重复执行多少次测试,每个测试用例都会拿到新鲜有效的Token,之前的"jwt malformed"错误再也没出现过。
如果你也遇到了类似的Mocha测试中认证Token失效或未正确赋值的问题,可以试试调整钩子的执行时机,把需要每次都确保有效的依赖操作(比如获取Token)放到beforeEach里。
内容的提问来源于stack exchange,提问作者Frank Uhal

