Jasmine隔离测试subscribe内方法调用校验失败问题排查
Jasmine隔离测试subscribe回调校验失败排查
核心报错原因
测试失败是4个明确的编码错误导致的,和Jasmine本身运行机制无关:
- 构造函数传参顺序错位:
MugComponent构造函数的参数顺序是constants -> mugService -> messageService -> route -> router,测试实例化组件时漏掉了mugService参数,直接把messageService传到了第二个参数位,依赖注入完全错位,组件内部拿不到正确的service实例。 - 未mock
mugService.saveMug返回值:saveMug返回的是Observable流,没有给这个方法mock合法的Observable返回值的话,subscribe注册的回调永远不会触发,回调内部的showMessage自然不会执行。 - Spy挂载对象错误:
showMessage是注入的messageService实例上的方法,不是MugComponent自身的方法,给component.showMessage打spy完全监听不到实际调用。 - 未初始化
subscriptions对象:组件代码里调用了this.subscriptions.add(sub),测试实例化组件后没有手动初始化这个属性,代码执行到这一行会直接抛出属性不存在的错误。
修复后的隔离测试代码
完全不依赖TestBed、fixture,符合纯隔离测试要求:
import { of } from 'rxjs'; import { Subscription } from 'rxjs'; // 其余业务依赖导入省略:MugComponent、Constants、Mug、相关service类型声明 describe("MugComponent", () => { let component: MugComponent; const constants: Constants = new Constants(); // 1. mock所有依赖服务,声明需要监听的方法 const mugService = jasmine.createSpyObj("mugService", ["saveMug"]); const messageService = jasmine.createSpyObj("messageService", ["showMessage"]); const route = jasmine.createSpyObj("ActivatedRoute", [], {}); const router = jasmine.createSpyObj("Router", ["navigate"]); beforeEach(() => { // 2. 严格按照构造函数参数顺序传入依赖 component = new MugComponent( constants, mugService, messageService, route, router ); // 3. 初始化组件内部用到的订阅容器 component.subscriptions = new Subscription(); component.mug = new Mug(); // 4. 给service方法mock返回值,模拟接口正常返回的Observable mugService.saveMug.and.returnValue(of({ dates: [] // 按照Mug类构造要求传入模拟值即可 })); }); it("saveMug传入previous=true时应该调用showMessage展示对应提示", () => { component.saveMug(true); // 5. 校验messageService实例上的方法调用,而非component自身的属性 expect(messageService.showMessage).toHaveBeenCalledOnceWith( constants.MUG, [constants.NO_MUG] ); // 可额外校验mug重置逻辑 expect(component.mug).toBeInstanceOf(Mug); }); });
补充说明
如果mugService.saveMug返回的是带延迟的异步Observable,只需要把测试用例包在fakeAsync执行域里,调用完组件方法后执行tick()模拟时间流逝,再做断言即可,不需要引入额外测试工具。
内容的提问来源于stack exchange,提问作者Ivan Martin Blanco
相关产品推荐
相关产品推荐

