Angular测试:Mock服务桩传复杂对象 解决嵌套属性undefined报错
问题根因
这问题和of()的内部运行机制没有关系,核心错误是你没有订阅getUser()返回的Observable,直接把Observable实例当成了实际返回的用户对象使用。
你认为前两行执行正常完全是错觉:
- 第一行把
of()创建的Observable实例赋值给this.activeTokens,属于合法赋值操作,不会报错 - 第二行访问Observable实例上不存在的
username属性,拿到的值是undefined,将undefined赋值给类属性本身不会抛错,只是值完全不符合预期,你没有做值校验就误以为执行成功 - 到第三行时,你先读取
this.activeTokens.attributes,Observable实例上没有这个自定义属性,拿到undefined,再继续读取undefined上的organizationId属性,就触发了你看到的类型错误,和你mock数据中定义的attributes字段没有任何关联。
代码修正方案
根据你的服务设计选择对应写法即可:
场景1:真实服务的getUser()为异步返回Observable
这是Angular服务处理token逻辑的常规设计,你必须在订阅回调中拿到Observable发射的实际值,所有依赖用户对象的逻辑都要放到订阅回调里:
ngOnInit(): void { this.token.getUser().subscribe(currentUser => { this.activeTokens = currentUser; this.username = currentUser.username; this.organization.id = currentUser.attributes.organizationId; // 其余依赖用户信息的赋值逻辑统一放在这里 }); }
记得在组件的ngOnDestroy生命周期里取消订阅,避免内存泄漏。
场景2:真实服务的getUser()为同步返回本地存储值
如果你的服务不需要异步处理,是直接同步返回本地缓存的token对象,那测试桩里多余包了一层of(),直接返回对象即可:
tokenStorageServiceStub = { // Mock service data getUser: () => ({ username: 'organization-a-a-normal', roles: ['PROCESSOR'], attributes: { organizationId: 111, organizationName: 'Org.name Aa', userFullName: 'User for organization A-A', userId: 22, }, access_token: 'AT01', refresh_token: 'RT01', token_type: 'Bearer', expires_in: 3600, }), };
关于
of()的废弃说明 你听到的废弃传言不准确。目前RxJS官方没有废弃of()创建函数的计划,只是在v7版本移除了of()支持传入resultSelector参数的重载,传入普通对象、基础类型值直接发射的用法完全稳定,测试中用它mock同步返回的异步数据流没有任何问题。
测试桩Mock服务的最佳实践
- 严格对齐真实服务的返回类型:真实方法返回Observable就用
of()/throwError()等返回对应流,真实方法同步返回值就直接返回值,不要随意改变返回类型 - 优先用Spy创建mock方法,方便不同测试用例自定义返回值、校验调用情况,比写死stub灵活度高很多:
// 初始化时创建带spy的mock服务 tokenStorageServiceStub = jasmine.createSpyObj('TokenStorageService', ['getUser']); // 单个测试用例中自定义返回值 it('should load user info on init', () => { const mockUser = { // 你的mock用户数据 }; tokenStorageServiceStub.getUser.and.returnValue(of(mockUser)); component.ngOnInit(); // 触发订阅后校验赋值逻辑 expect(component.username).toBe(mockUser.username); expect(component.organization.id).toBe(mockUser.attributes.organizationId); });
- 如果需要测试加载态、错误分支、多次触发的逻辑,可以用
Subject代替of(),手动控制流的发射、报错、完成时机,覆盖更多测试场景。
内容的提问来源于stack exchange,提问作者stackPete
相关产品推荐
相关产品推荐

