如何使用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
相关产品推荐
相关产品推荐

