Jest结合事件发射器的异步测试无法正常结束的问题求助
我现在在测试一个Node.js进程,它的逻辑是从Redis队列读取数据、处理后把结果推送到另一个Redis队列。为了做黑盒测试,我给数据库和缓存都做了mock,核心逻辑保持不变。其中Redis的mock在往输出队列执行lpush时会触发一个事件,我本来打算在测试里捕获这个事件来做断言检查。
现在的问题是:功能本身是正常的——事件确实触发了,测试里也捕获到了,数据也正确,但测试就是没法正常结束,甚至Jest会一直挂着,哪怕用了默认的5秒超时也没用。
我试过两种写法都不行,一种用done回调,另一种用Promise+async/await,虽然断言都通过了、日志也打印了,但Jest就是不退出。我不想去mock业务逻辑里的推送函数,想保持黑盒测试的完整性,有没有大佬能给点提示?
第一种用done的测试写法
describe('foo', () => { describe('bar', () => { const fakeData = [{ id: 1 }] const EE = new EventEmitter() beforeAll(() => { redisMock.new = jest.fn(() => ({ brpop: jest.fn(() => fakeData.pop()), lpush: jest.fn((destQueue, data) => EE.emit('lpush', { destQueue, data })), })) mylogicWhichUsesRedis.start() }) test('my test', (done) => { EE.once('lpush', (params) => { console.log(params) expect(params.destQueue).toEqual('something') console.log('test') // 这里能正常打印 done() }) }) }) })
第二种用Promise+async/await的测试写法
test('foobar', async () => { const params = await new Promise((resolve) => { EE.once('lpush', resolve) }) expect(params.destQueue).toEqual('something') console.log('this get printed') // 这里也能正常打印 })
可能的原因分析和解决方案
我猜大概率是你的mylogicWhichUsesRedis.start()启动了一个持续运行的进程(比如轮询Redis的循环),Jest在测试完成后没法自动终止这个进程,导致一直挂着。给你几个可行的解决思路:
在
afterAll里停止业务进程
给你的mylogicWhichUsesRedis加一个stop()方法,在测试结束后调用它来终止所有异步循环或连接,这是最规范的做法,能保证测试环境干净收尾:afterAll(() => { mylogicWhichUsesRedis.stop() })检查Redis mock的异步逻辑是否有残留
实际Redis的brpop是异步阻塞操作,你的业务逻辑里可能有基于它的轮询循环。虽然mock成了同步返回,但如果循环没有在数据耗尽后停止,就会一直占用线程,导致Jest无法退出。可以检查业务逻辑的轮询终止条件,确保mock数据用完后能停止循环。兜底方案:Jest参数强制退出
如果暂时没法修改业务逻辑,可以在运行测试时加上--forceExit参数,让Jest在测试完成后强制终止进程。但这是兜底手段,可能会掩盖潜在的资源泄漏问题,不推荐长期使用。
备注:内容来源于stack exchange,提问作者Andrea Franchini

