Node.js异步文件写入单元测试异常(Mocha+Chai)
解决Node.js文件写入后立即读取为空的异步问题
嘿,作为Node.js初学者,你遇到的这个问题太典型了——这完全是异步流操作的缓冲区特性搞的鬼!我来给你拆解清楚:
问题根源
你用fs.createWriteStream创建的是异步写入流,fss.write("bla")只是把数据放到了内存缓冲区里,并没有立刻写到磁盘上。fss.end()虽然触发了结束流程,但它的回调函数才是真正通知你“所有数据已经完全写入磁盘”的信号。
如果你的测试代码在调用fss.end()之后,没等回调执行就去读取文件,那此时磁盘上的文件还没被写入完成,自然读到空内容;而当整个Node.js进程结束时,它会自动清理所有未完成的流操作,把缓冲区里的内容刷到磁盘,所以你事后看文件是完整的。
解决方案
针对你的测试场景,推荐两种靠谱的写法:
1. 用回调确保写入完成后再读取(回调风格)
把你的读取逻辑放到fss.end()的回调函数里,确保写入完成后再执行读取:
const fs = require("fs"); function test(callback) { const fss = fs.createWriteStream("file.txt", { flags: 'a+' }); fss.write("bla"); fss.end(() => { // 写入完成后再读取 fs.readFile("file.txt", 'utf8', (err, data) => { if (err) return callback(err); callback(null, data); // 这里的data就是完整的"bla"了 }); }); } // 测试时调用(Mocha里可以用done回调) test((err, content) => { if (err) throw err; console.log(content); // 输出"bla" });
2. 转成Promise风格(更适合测试的async/await)
Node.js的fs/promises模块提供了Promise化的API,或者你可以用util.promisify包装流操作,这样在测试里用async/await会更清爽:
const fs = require("fs/promises"); const { createWriteStream } = require("fs"); const { promisify } = require("util"); // 把流的end操作转成Promise function writeToFile(content, path) { return new Promise((resolve, reject) => { const stream = createWriteStream(path, { flags: 'a+' }); stream.write(content); stream.on('finish', resolve); stream.on('error', reject); stream.end(); }); } // 测试用例(Mocha里直接用async函数) async function runTest() { await writeToFile("bla", "file.txt"); const content = await fs.readFile("file.txt", 'utf8'); console.log(content); // 输出"bla" } runTest();
3. Mocha测试里的正确写法
因为Mocha原生支持异步测试,你可以直接用async/await或者done回调来确保测试等待异步操作完成:
const { expect } = require("chai"); const fs = require("fs/promises"); const { createWriteStream } = require("fs"); function writeToFile(content, path) { return new Promise((resolve, reject) => { const stream = createWriteStream(path, { flags: 'a+' }); stream.write(content); stream.on('finish', resolve); stream.on('error', reject); stream.end(); }); } describe("文件写入测试", () => { it("写入后读取应得到完整内容", async () => { // 先清空文件(可选,避免测试残留) await fs.writeFile("file.txt", ""); // 执行写入 await writeToFile("bla", "file.txt"); // 读取并断言 const content = await fs.readFile("file.txt", 'utf8'); expect(content).to.equal("bla"); }); });
额外提示
如果你用TypeScript,写法几乎一致,只需要给变量加上类型声明即可,比如:
import { createWriteStream } from "fs"; import * as fs from "fs/promises"; async function writeToFile(content: string, path: string): Promise<void> { // ...和JS一样的逻辑 }
内容的提问来源于stack exchange,提问作者Drop
相关产品推荐
相关产品推荐

