You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:14:53