Mocha中before()块获取的token传递到it()时为undefined的解决方法
问题根因
变量取到undefined是两个错误共同导致的:
before()钩子中调用的request是异步回调风格函数,你没有告知Mocha需要等待该异步请求执行完成,Mocha跑完request()调用语句后不会等回调逻辑执行,直接进入后续it()测试用例,此时token还没完成赋值。- 测试用例里写的
authToken = this.token属于取值逻辑错误:你把token存在了外层声明的let token变量中,根本没有往Mocha的this上下文挂载token属性,自然取到undefined。
修复方案
两种改法任选其一即可,推荐第一种,更符合当前异步代码的编写习惯:
方法1:将before钩子改为async/await风格,适配异步等待逻辑
request库已经停止官方维护,你可以借助Node自带的promisify工具把回调风格的request转成Promise版本,同时删掉错误的this.token取值逻辑,直接使用外层作用域的token变量。
修复后完整代码如下:
const { spec } = require('pactum'); const { promisify } = require('util'); const request = require('request'); const userConfig = require('../config/credential.json') // 将回调版request包装为Promise版本 const requestPromise = promisify(request); process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; let token = ""; describe('POST /jobs', function () { // 标记为async函数,让Mocha自动等待内部异步操作执行完成 before(async function () { const option = { 'method': 'POST', 'url': 'https://login.microsoftonline.com/<tenant_id>/oauth2/v2.0/token', 'headers': { 'Content-Type': 'application/x-www-form-urlencoded' }, form: { 'client_id': `${userConfig.client_id}`, 'grant_type': 'password', 'Password': `${userConfig.Password}`, 'UserName': `${userConfig.UserName}`, 'scope': `${userConfig.scope}`, 'client_secret': `${userConfig.client_secret}` } }; try { const response = await requestPromise(option); const modifyResponse = JSON.parse(response.body) token = modifyResponse["access_token"] } catch (error) { throw new Error(error) } }) it('Create Job should return 201 status code', async () => { // 直接取外层赋值完成的token即可,不需要通过this访问 console.log('------------>', token) await spec() .post('https://localhost:3000/jobs') .withHeaders('Authorization', `Bearer ${token}`) .withHeaders('X-Client-Id', 'AzureDataLoaderUI') .withHeaders('X-Message-Created-Ts', '2022-JUN-07 04:06') .withHeaders('X-Message-Id', '4e8fb14c-c71f-46f8-b41a-561d393037ce') .withHeaders('X-Transaction-Created-Ts', '2022-JUN-07 04:06') .withHeaders('X-Transaction-Id', 'f11c97dc-a6eb-4ff7-aff6-de47c4686941') .withHeaders('X-User-Id', 'AutoTestUser') .withHeaders('Ocp-Apim-Subscription-Key', 'a731ccf0510849c690d23bb42ecef9d3') .withHeaders('aeg-sas-key', 'b6LrYspQUAY2XnxkrDFDFDYsb8U2zqMQAzaQoZcQ=') .withJson({ "product": "IRT", "entity": "CENTRE PROVISIONING AND RECIPIENT DETAILS", "operation": "UPDATE", "jobName": "AUTOPERF02", "studyId": "AUTO02" }) .expectStatus(201); }); });
方法2:保留回调写法,通过Mocha的done回调标记异步完成
如果你不想修改为Promise写法,可以在before钩子的入参中加入done回调函数,在request请求的回调逻辑末尾手动调用done(),告知Mocha前置操作执行完毕,可以运行测试用例,同时修正token的取值逻辑,不要访问this.token。
对应的before块写法如下:
before(function (done) { var option = { 'method': 'POST', 'url': 'https://login.microsoftonline.com/<tenant_id>/oauth2/v2.0/token', 'headers': { 'Content-Type': 'application/x-www-form-urlencoded' }, form: { 'client_id': `${userConfig.client_id}`, 'grant_type': 'password', 'Password': `${userConfig.Password}`, 'UserName': `${userConfig.UserName}`, 'scope': `${userConfig.scope}`, 'client_secret': `${userConfig.client_secret}` } }; request(option, function (error, response) { if (error) { done(error) return } const modifyResponse = JSON.parse(response.body) token = modifyResponse["access_token"] // 调用done通知Mocha前置流程结束 done() }) })
额外注意
- 如果你坚持要用
this.xxx的方式在钩子和用例之间传值,注意不能把before和it的回调写成箭头函数——箭头函数会绑定外层作用域的this,无法拿到Mocha提供的测试上下文对象,必须写成function() {}的普通函数形式,赋值时写this.token = modifyResponse["access_token"],用例内直接取this.token即可。 - 旧版
request库已经停止维护,后续建议替换为axios、node-fetch或者pactum自带的请求方法,减少回调风格带来的异步时序问题。
内容的提问来源于stack exchange,提问作者MAX GRIMM
相关产品推荐
相关产品推荐

