Mocha.js测试在JSON文件创建前提前完成的问题求助
问题分析
你遇到的核心问题是异步操作的时序不匹配:DataHandler构造函数里调用的update()方法依赖了异步的request请求,当测试代码执行到断言逻辑时,异步请求还没完成,testData.json文件还没被创建出来,所以第一次测试会失败;第二次运行时,上一次测试的异步请求已经完成,文件已经存在,测试自然就通过了。
下面是具体的修复方案,分两步走:
步骤1:重构DataHandler类,处理异步逻辑
因为request是异步函数,我们需要把update方法改造成支持等待的形式,同时调整构造函数的逻辑(注意:构造函数不能直接声明为async,所以我们可以在类里保存一个初始化Promise,供外部测试代码等待)。
方案A:手动用Promise封装原生request
const fs = require('fs'); const request = require('request'); class DataHandler{ constructor(){ // 保存初始化Promise,让外部可以等待异步操作完成 this.initPromise = this.init(); } async init(){ if(await this.shouldUpdate()) { await this.update(); } } async shouldUpdate(){ try { // 用异步方法检查文件是否存在 await fs.promises.access("testData.json"); const file = await fs.promises.readFile("testData.json"); const parsed = JSON.parse(file); const oldDT = Date.parse(parsed['updated_date']); const dateDifference = Date.now() - oldDT; const minutes = Math.floor(dateDifference / 60000); return minutes > 2; } catch (err) { // 文件不存在时,返回true触发更新 return true; } } update(){ return new Promise((resolve, reject) => { request("http://someurl.com/data.json", function (error, response, body) { if (error || response.statusCode !== 200) { return reject(error || new Error(`请求失败,状态码:${response.statusCode}`)); } const parsedJSON = JSON.parse(body); parsedJSON['updated_date'] = new Date(); const json = JSON.stringify(parsedJSON); // 用异步写入文件,避免阻塞 fs.writeFile("testData.json", json, (err) => { if (err) reject(err); else resolve(); }); }); }); } } module.exports = DataHandler;
方案B:改用request-promise简化代码
如果你可以引入request-promise库(执行npm install request-promise安装),代码会更简洁:
const fs = require('fs').promises; const rp = require('request-promise'); class DataHandler{ constructor(){ this.initPromise = this.init(); } async init(){ if(await this.shouldUpdate()) { await this.update(); } } async shouldUpdate(){ try { await fs.access("testData.json"); const file = await fs.readFile("testData.json"); const parsed = JSON.parse(file); const oldDT = Date.parse(parsed['updated_date']); const dateDifference = Date.now() - oldDT; const minutes = Math.floor(dateDifference / 60000); return minutes > 2; } catch (err) { return true; } } async update(){ const body = await rp("http://someurl.com/data.json"); const parsedJSON = JSON.parse(body); parsedJSON['updated_date'] = new Date(); const json = JSON.stringify(parsedJSON); await fs.writeFile("testData.json", json); } } module.exports = DataHandler;
步骤2:调整测试代码,确保干净的测试环境并等待异步完成
测试前要先清理残留的testData.json文件,保证每次测试都从干净状态开始,然后等待DataHandler的异步初始化完成后再执行断言:
const chai = require('chai'); chai.use(require('chai-json')); const DataHandler = require('../handler'); const expect = chai.expect; const path = require('path'); const fs = require('fs').promises; // 用绝对路径避免工作目录不同导致的路径错误 const testFilePath = path.resolve(__dirname, '../testData.json'); describe('#json handling', function(){ // 每次测试前清理目标文件 beforeEach(async function(){ try { await fs.unlink(testFilePath); } catch (err) { // 文件不存在时忽略错误,避免测试失败 if (err.code !== 'ENOENT') throw err; } }); it('should create a testData.json file', async function(){ const handler = new DataHandler(); // 等待异步初始化和文件创建完成 await handler.initPromise; expect(testFilePath).to.be.a.jsonFile(); }) })
额外小贴士
- 尽量使用绝对路径处理文件,避免因为测试运行的工作目录不同导致路径错误。
- 如果外部API不稳定,推荐用
nock库模拟HTTP请求,让测试不依赖外部服务的可用性。 - 如果你不需要异步逻辑,也可以改用
request的同步版本,但异步方式更符合Node.js的非阻塞设计理念。
内容的提问来源于stack exchange,提问作者hcphoon
相关产品推荐
相关产品推荐

