如何让Jest等待NATS Streaming连接事件触发后再执行断言以验证Listener调用?
解决Jest等待NATS Streaming连接事件触发的问题
这个问题本质是Jest默认同步执行测试流程,而NATS的connect事件是异步触发的,所以我们需要让Jest等待这个异步事件完成后再执行断言。下面给你几种可行的解决方案:
方案1:重构NatsHelper.connect方法返回Promise
把原来的回调式API改成Promise风格,这样测试时可以用async/await直接等待连接完成:
import nats, { Stan } from 'node-nats-streaming' import { randomBytes } from 'crypto' class NatsHelper { private client: Stan | null = null public connect(url: string, clusterID: string, clientID: string, verboseConnection: boolean = true): Promise<Stan> { return new Promise((resolve, reject) => { const clientIDString = `${clientID}-${randomBytes(4).toString('hex')}` if (verboseConnection) { console.log(`Connecting to NATS cluster '${clusterID}' with clientID '${clientIDString}' on url '${url}'`) } const connectionAttempt = nats.connect( clusterID, clientIDString, { url } ) const setupConnection = () => { this.client = connectionAttempt this.client.on('close', () => { if (verboseConnection) { console.log(`Connection with NATS cluster '${clusterID}' with clientID '${clientIDString}' on url '${url}' was closed`) } this.client = null process.exit() }) process.on('SIGINT', () => this.client?.close()) process.on('SIGTERM', () => this.client?.close()) if (verboseConnection) { console.log(`Connected to NATS cluster '${clusterID}' with clientID '${clientIDString}' on url '${url}' successfully`) } resolve(connectionAttempt) } connectionAttempt.on('connect', setupConnection) // 处理连接错误的情况 connectionAttempt.on('error', (err) => reject(err)) }) } // 保留原回调风格的API(可选) public connectWithCallback(url: string, clusterID: string, clientID: string, listener: (...args: any[]) => void, verboseConnection: boolean = true): void { this.connect(url, clusterID, clientID, verboseConnection) .then((client) => listener(client)) .catch((err) => listener(err)) } }
对应的测试代码可以这样写(使用async/await):
import { NatsHelper } from './nats-helper' import nats from 'node-nats-streaming' // Mock NATS库,避免真实连接 jest.mock('node-nats-streaming') describe('NatsHelper', () => { let helper: NatsHelper let mockStan: jest.Mocked<Partial<Stan>> beforeEach(() => { // 创建Mock的Stan实例,模拟connect事件触发 mockStan = { on: jest.fn((event, cb) => { if (event === 'connect') { // 异步触发connect事件 process.nextTick(cb) } return mockStan }), close: jest.fn() } ;(nats.connect as jest.Mock).mockReturnValue(mockStan) helper = new NatsHelper() }) it('should resolve when connected successfully', async () => { const client = await helper.connect('test-url', 'test-cluster', 'test-client') expect(client).toBe(mockStan) expect(nats.connect).toHaveBeenCalledWith('test-cluster', expect.any(String), { url: 'test-url' }) }) })
方案2:不修改原方法,用Jest的异步测试机制
如果你不想改动现有代码,可以利用Jest的done回调或者Promise包裹测试逻辑,等待listener被调用:
使用done回调
it('should call the listener when NATS connects', (done) => { const mockListener = jest.fn(() => { // 断言listener被调用 expect(mockListener).toHaveBeenCalled() // 通知Jest测试完成 done() }) helper.connect('test-url', 'test-cluster', 'test-client', mockListener) })
使用Promise包裹
it('should call the listener when NATS connects', async () => { const mockListener = jest.fn() // 用Promise等待listener被调用 await new Promise<void>((resolve) => { helper.connect('test-url', 'test-cluster', 'test-client', (...args) => { mockListener(...args) resolve() }) }) expect(mockListener).toHaveBeenCalled() })
关键注意点:Mock第三方库
无论用哪种方案,都必须Mocknode-nats-streaming库,否则测试会尝试真实连接NATS服务器,既慢又不稳定。上面的示例中已经用到了jest.mock('node-nats-streaming'),并创建了模拟的Stan实例来触发connect事件。
内容的提问来源于stack exchange,提问作者Estêvão E. Calheira
相关产品推荐
相关产品推荐

