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

如何使用Jasmine对Chargebee-TypeScript相关Firebase云函数做单元测试?

问题描述

我有一个用于检查邮箱是否存在于Chargebee中的Firebase云函数,核心实现代码如下:

const cbCmd = chargeBee.customer.list({ email: { is: email }, include_deleted: false, limit: 1 });
const callbackResolver = new Promise<any>((resolve, reject) => {
  void cbCmd.request((err: any, res: WrappedListCustomerResp) => {
     if (err) {
       reject(err);
     }
     resolve(!res.list.find(payee => payee.customer.email === email));
     });
  });
return Promise.resolve(callbackResolver);

本质上,cbCmd包含一个名为request的方法,该方法最终会发起API请求。request接收一个函数,用于定义对Chargebee返回数据的转换逻辑(Chargebee的TypeScript包文档未完整描述返回内容,因此我自行研究返回数据类型并定义了接口WrappedListCustomerResp)。请问如何使用Jasmine对该函数进行单元测试?


Jasmine单元测试方案

要测试这个函数,核心是Mock Chargebee的customer.list和request方法,避免发起真实API请求,同时覆盖三种核心场景:邮箱存在、邮箱不存在、API调用失败。

1. 准备Mock与测试环境

首先要Mock chargeBee模块,模拟它的customer.list方法返回带有request方法的对象,以此控制回调的触发逻辑。假设待测试函数名为checkEmailExistsInChargebee:

// 模拟Chargebee返回数据的类型
type MockPayee = {
  customer: { email: string };
};

type MockWrappedListResp = {
  list: MockPayee[];
};

// 创建Chargebee的Jasmine Spy对象
const chargeBeeSpy = jasmine.createSpyObj('chargeBee', {
  'customer.list': jasmine.createSpyObj('listCommand', ['request'])
});

// 若函数依赖全局chargeBee对象,需在测试前替换为Spy
global.chargeBee = chargeBeeSpy;

2. 测试场景1:邮箱已存在于Chargebee

当Chargebee返回包含目标邮箱的用户列表时,函数应返回false(因为!res.list.find(...)会取反):

it('returns false when email exists in Chargebee', (done) => {
  const testEmail = 'test@example.com';
  
  // 模拟request方法触发成功回调,返回包含目标邮箱的列表
  chargeBeeSpy.customer.list.and.returnValue({
    request: (callback: (err: any, res: MockWrappedListResp) => void) => {
      callback(null, {
        list: [{ customer: { email: testEmail } }]
      });
    }
  });

  checkEmailExistsInChargebee(testEmail)
    .then(result => {
      expect(result).toBe(false);
      // 验证Chargebee的list方法调用参数是否符合预期
      expect(chargeBeeSpy.customer.list).toHaveBeenCalledWith({
        email: { is: testEmail },
        include_deleted: false,
        limit: 1
      });
      done();
    })
    .catch(done.fail);
});

3. 测试场景2:邮箱不存在于Chargebee

当Chargebee返回的列表中没有目标邮箱时,函数应返回true:

it('returns true when email does NOT exist in Chargebee', (done) => {
  const testEmail = 'test@example.com';
  
  // 模拟request返回不包含目标邮箱的列表
  chargeBeeSpy.customer.list.and.returnValue({
    request: (callback: (err: any, res: MockWrappedListResp) => void) => {
      callback(null, {
        list: [{ customer: { email: 'other@example.com' } }]
      });
    }
  });

  checkEmailExistsInChargebee(testEmail)
    .then(result => {
      expect(result).toBe(true);
      done();
    })
    .catch(done.fail);
});

4. 测试场景3:Chargebee API调用出错

当API请求返回错误时,函数应reject该错误:

it('rejects with error when Chargebee API call fails', (done) => {
  const testEmail = 'test@example.com';
  const testError = new Error('API request timeout');
  
  // 模拟request触发错误回调
  chargeBeeSpy.customer.list.and.returnValue({
    request: (callback: (err: any, res: MockWrappedListResp) => void) => {
      callback(testError, null);
    }
  });

  checkEmailExistsInChargebee(testEmail)
    .catch(error => {
      expect(error).toBe(testError);
      done();
    })
    .catch(done.fail);
});

关键注意事项

  • 确保Mock的request方法正确触发传入的回调函数,这是Promise能正确resolve/reject的核心。
  • 验证chargeBee.customer.list的调用参数,确保函数的入参逻辑符合预期。
  • 如果是Firebase HTTPS云函数,可将邮箱检查逻辑单独抽离为纯函数测试,再额外测试HTTP请求的处理逻辑,降低耦合度。

内容的提问来源于stack exchange,提问作者Joseph Zabinski

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 11:20:33