Jest中带done回调的测试在前序测试失败后异常失败问题
Jest单条带done回调的测试失败导致后续所有测试失败的原因与解决方法
核心原因
当expect断言失败时会直接抛出异常,中断当前测试函数的执行,导致后续的done()函数从未被调用:
- Jest会将没有调用
done()的异步测试标记为超时失败 - 前序测试未做上下文清理(比如map实例未销毁、jest mock未还原、事件监听残留)会进一步污染后续测试的运行环境,最终表现为所有带done的测试全部失败
问题代码示例
可正常运行的原始测试
describe('Map#isRotating', () => { test('returns false by default', done => { const map = createMap(); expect(map.isRotating()).toBe(false); map.remove(); done(); }); test('returns true during a camera rotate animation', done => { const map = createMap(); map.on('rotatestart', () => { expect(map.isRotating()).toBe(true); }); map.on('rotateend', () => { expect(true).toBe(false); map.remove(); done(); }); map.rotateTo(5, {duration: 0}); }); test('returns true when drag rotating', done => { const map = createMap(); // Prevent inertial rotation. jest.spyOn(browser, 'now').mockImplementation(() => { return 0; }); map.on('rotatestart', () => { expect(map.isRotating()).toBe(true); }); map.on('rotateend', () => { expect(map.isRotating()).toBe(false); map.remove(); done(); }); simulate.mousedown(map.getCanvas(), {buttons: 2, button: 2}); map._renderTaskQueue.run(); simulate.mousemove(map.getCanvas(), {buttons: 2, clientX: 10, clientY: 10}); map._renderTaskQueue.run(); simulate.mouseup(map.getCanvas(), {buttons: 0, button: 2}); map._renderTaskQueue.run(); }); });
修改后触发批量失败的测试代码
describe('Map#isRotating', () => { test('returns false by default', done => { const map = createMap(); expect(true).toBe(false); map.remove(); done(); });
修复方案
方案1:给done回调的测试增加try/catch包裹
保证无论断言是否成功,done()都会被调用,错误会通过done()的参数传递给Jest做标记:
test('returns false by default', done => { try { const map = createMap(); expect(true).toBe(false); map.remove(); done(); } catch (err) { done(err); } });
方案2:改用async/await异步写法(更推荐)
Jest原生支持async异步测试,不需要手动调用done回调,断言失败会自动终止测试并标记结果,不会出现回调未执行的问题:
test('returns false by default', async () => { const map = createMap(); expect(map.isRotating()).toBe(false); map.remove(); });
方案3:增加全局清理逻辑
在afterEach钩子中统一做上下文清理,避免前序测试的残留内容影响后续用例:
afterEach(() => { // 还原所有jest mock jest.restoreAllMocks(); // 销毁残留的map实例 if (map) map.remove(); })
内容的提问来源于stack exchange,提问作者astridx
相关产品推荐
相关产品推荐

