You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 00:07:28