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

TypeScript测试云函数:如何Stub外部依赖类方法并捕获调用参数?

测试TypeScript编写的Google Cloud Function:验证publishMessage参数

问题

我想要测试一个用TypeScript编写的Google Cloud Function,该函数调用了Topic类的成员方法publishMessage,需要让单元测试捕获传递给publishMessage的参数并验证。

我试过用sinon或proxyquire实现,但相关示例都是JavaScript的require()语法,而且Stub的是模块级静态函数,不知道怎么适配TypeScript的import语法,也不清楚如何Stub类内部的方法。

要测试的代码大致如下:

import {PubSub} from "@google-cloud/pubsub"

class Foo {
    private pubSub = new PubSub()
    private topic = this.pubSub.topic("foo-topic")

    public publishMessage() {
        const data = Buffer.from("content")
        return this.topic.publishMessage({data})
    }
}

const foo = new Foo()

// 测试时会调用这个导出的函数
export const fooPublishMessage = foo.publishMessage

当前用mocha和chai测试,也可以用其他工具。

解决方案

第一步:改造代码,支持依赖注入

原代码中直接在类内部实例化PubSub,属于硬编码依赖,无法直接替换为测试用的Stub对象。首先要调整Foo类的结构,通过构造函数注入Topic实例,让测试可以灵活替换依赖:

import {PubSub, Topic} from "@google-cloud/pubsub"

class Foo {
    private topic: Topic

    // 构造函数可选注入Topic实例,生产环境用默认逻辑初始化
    constructor(topic?: Topic) {
        if (topic) {
            this.topic = topic
        } else {
            const pubSub = new PubSub()
            this.topic = pubSub.topic("foo-topic")
        }
    }

    public publishMessage() {
        const data = Buffer.from("content")
        return this.topic.publishMessage({data})
    }
}

// 生产环境使用默认构造
const foo = new Foo()
export const fooPublishMessage = foo.publishMessage

方法一:用Sinon Stub Topic方法

结合Sinon和chai,创建Stub版的Topic对象,验证publishMessage的调用参数:

  1. 安装依赖:
npm install sinon @types/sinon --save-dev
  1. 测试代码:
import { expect } from 'chai'
import sinon from 'sinon'
import { Topic } from '@google-cloud/pubsub'
import { Foo } from '../src/foo'

describe('Foo.publishMessage', () => {
    it('调用publishMessage时传入正确的data参数', async () => {
        // 创建publishMessage方法的Stub,模拟返回值
        const publishStub = sinon.stub().resolves('fake-message-id')
        // 构造符合Topic类型的Stub对象
        const mockTopic = {
            publishMessage: publishStub
        } as unknown as Topic

        // 传入Stub的Topic实例创建Foo
        const foo = new Foo(mockTopic)
        await foo.publishMessage()

        // 验证调用次数和参数
        expect(publishStub.calledOnce).to.be.true
        const [callParam] = publishStub.firstCall.args
        expect(callParam.data.toString()).to.equal('content')
    })
})

方法二:用ts-mockito实现类型友好的Mock

如果希望测试代码更贴合TypeScript的类型系统,推荐使用ts-mockito,它能自动推断类型,无需手动断言:

  1. 安装依赖:
npm install ts-mockito @types/ts-mockito --save-dev
  1. 测试代码:
import { expect } from 'chai'
import { mock, instance, verify, when } from 'ts-mockito'
import { Topic } from '@google-cloud/pubsub'
import { Foo } from '../src/foo'

describe('Foo.publishMessage', () => {
    it('向publishMessage传递正确的data参数', async () => {
        // 创建Topic的Mock实例
        const mockTopic = mock(Topic)
        // 模拟publishMessage的返回逻辑
        when(mockTopic.publishMessage({ data: Buffer.from('content') })).thenResolve('fake-id')

        // 传入Mock实例创建Foo
        const foo = new Foo(instance(mockTopic))
        await foo.publishMessage()

        // 验证publishMessage被正确调用
        verify(mockTopic.publishMessage({ data: Buffer.from('content') })).once()
    })
})

为什么不直接Stub PubSub模块?

直接Stub模块级的PubSub构造函数需要用到proxyquire或rewire这类工具,但它们对TypeScript的支持不够友好,测试代码会更复杂。依赖注入的方式不仅让测试更清晰,还符合SOLID原则,提升了代码的可维护性。

内容的提问来源于stack exchange,提问作者Christopher Simmons

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 14:35:58