如何测试预期抛出异常的异步方法?Mocha测试未生效
你的测试没捕获到预期异常的核心原因是:invoke方法里的错误没有被正确关联到它返回的Promise上。
为什么会出现这个问题?
你标记了invoke为async函数,所以它会自动返回一个Promise,但你内部用的是回调式的fs.readFile——当fs.readFile调用完成后(只是发起了读取请求,还没执行回调),async函数就已经执行完毕,返回的Promise被直接resolve为undefined了。而回调里抛出的错误属于另一个异步调用栈,根本无法被这个Promise捕获,所以Mocha会认为Promise被fulfilled,而不是rejected。
修复方案:让错误正确关联到Promise
有两种常见的修复方式,选哪种都可以:
方案1:使用Promise版的文件读取API
Node.js的fs模块已经提供了Promise风格的API(fs.promises),直接用它就能让错误自动被Promise捕获:
import fs from 'fs/promises'; public async invoke(): Promise<void> { try { await fs.readFile(this.templatePath); } catch (outerError) { throw new Error(`FileNotFoundException: Cannot find file \`${this.templatePath}'.`); } }
这样,当文件不存在时,fs.promises.readFile会返回一个rejected的Promise,await会把这个错误抛出来,我们自定义错误信息后再抛出,这个错误会被async函数返回的Promise捕获,测试就能正确断言了。
方案2:手动用Promise包裹回调式API
如果你不想切换到Promise版API,可以手动把回调式的fs.readFile包裹在Promise里:
import fs from 'fs'; public async invoke(): Promise<void> { return new Promise((resolve, reject) => { fs.readFile(this.templatePath, (outerError: NodeJS.ErrnoException, data: Buffer): void => { if (outerError !== null) { reject(new Error(`FileNotFoundException: Cannot find file \`${this.templatePath}'.`)); return; } resolve(); }); }); }
这里我们手动创建了一个Promise,当文件读取失败时调用reject传递错误,成功时调用resolve。这样invoke返回的Promise就会正确关联到异步操作的结果,测试就能捕获到rejected状态了。
测试代码无需修改
你的原测试代码是没问题的,只要修复了invoke方法,它就能正确断言Promise被Error拒绝:
it("should throw when the template file does not exist", async (): Promise<void> => { await expect(new UpdateReadmeCommandlet().invoke()).to.be.rejectedWith(Error); });
这样修改后,Mocha就能正确捕获到预期的异常,不会再出现AssertionError了,而且也不会影响其他测试的执行。
内容的提问来源于stack exchange,提问作者Christian Ivicevic

