Angular单元测试:如何Mock并注入带依赖的rxfire httpsCallable函数
Angular单元测试:Mock RxFire的httpsCallable及Firebase Functions依赖
问题根源
你碰到的functionInstance._url is not a function错误,核心原因是组件依赖的this.functions是真实Firebase Functions实例,即便Mock了httpsCallable,它内部仍会调用实例的私有方法(如_url)。另外,你用spyOnPropertyMock导出函数的方式有误,httpsCallable是rxfire模块导出的函数,并非属性。
正确实现方案
1. 先Mock Firebase Functions实例
在测试模块中提供Mock的Functions对象,规避真实实例的内部方法调用:
import { Functions } from '@angular/fire/functions'; // 创建Mock Functions对象,模拟所需私有方法/属性 const mockFunctions = jasmine.createSpyObj('Functions', [], { _url: jasmine.createSpy().and.returnValue('mock-api-endpoint-url') }); beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [YourTargetComponent], providers: [ // 注入Mock的Functions实例 { provide: Functions, useValue: mockFunctions } ] }).compileComponents(); });
2. 正确Mock RxFire的httpsCallable函数
httpsCallable是模块导出的函数,需用spyOnMock整个模块的导出成员:
import * as rxfire from 'rxfire'; import { of } from 'rxjs'; beforeEach(() => { // 重置Spy,避免测试用例间状态污染 spyOn(rxfire, 'httpsCallable').and.callFake(() => { // 返回模拟的可调用函数,支持接收参数并返回Observable return (formData: any) => { // 默认返回成功响应,后续可按需修改 return of({ message: '表单提交成功' }); }; }); });
3. 编写多场景测试用例
测试成功响应场景
it('调用saveForm时应打印成功响应', () => { spyOn(console, 'log'); const fixture = TestBed.createComponent(YourTargetComponent); const component = fixture.componentInstance; component.saveForm({ username: 'test', email: 'test@example.com' }); // 验证控制台打印成功响应 expect(console.log).toHaveBeenCalledWith({ message: '表单提交成功' }); // 验证httpsCallable被传入正确参数调用 expect(rxfire.httpsCallable).toHaveBeenCalledWith(mockFunctions, 'api/endpoint'); });
测试错误响应场景
临时修改httpsCallable的返回值为错误Observable:
import { throwError } from 'rxjs'; it('当接口调用失败时应打印错误信息', () => { spyOn(console, 'log'); const fixture = TestBed.createComponent(YourTargetComponent); const component = fixture.componentInstance; // 覆盖默认Spy,返回错误Observable spyOn(rxfire, 'httpsCallable').and.callFake(() => { return (formData: any) => { return throwError(() => new Error('权限不足,无法提交')); }; }); component.saveForm({ username: 'test', email: 'test@example.com' }); expect(console.log).toHaveBeenCalledWith(new Error('权限不足,无法提交')); });
4. 可选:封装Mock工具函数
若多个测试用例需要重复Mock,可封装成工具函数提升复用性:
import { of, throwError } from 'rxjs'; import * as rxfire from 'rxfire'; function mockHttpsCallable(response: any | Error) { spyOn(rxfire, 'httpsCallable').and.callFake(() => { return (data: any) => { return response instanceof Error ? throwError(() => response) : of(response); }; }); } // 测试用例中调用示例 it('处理自定义成功响应', () => { mockHttpsCallable({ status: 200, data: { formId: '1001' } }); const fixture = TestBed.createComponent(YourTargetComponent); const component = fixture.componentInstance; component.saveForm({ username: 'test' }); // 后续验证逻辑... });
内容的提问来源于stack exchange,提问作者Vicky
相关产品推荐
相关产品推荐

