使用NgRx MockStore时模板报Cannot read properties of undefined (reading 'id')错误
解决单元测试中Angular模板的
Cannot read properties of undefined (reading 'id')错误 问题场景
应用本身运行正常,但执行单元测试时抛出以下错误:
TypeError: Cannot read properties of undefined (reading 'id')
错误指向模板中使用async管道的代码行:
*ngIf="customer$ | async as customer"
完整错误栈:
debug.js:21 TypeError: Cannot read properties of undefined (reading 'id') at templateFn (template.html:12:23) at executeTemplate (_karma_webpack_/webp…20/core.mjs:12084:9) at refreshView (_karma_webpack_/webp…0/core.mjs:11969:13) at refreshComponent (_karma_webpack_/webp…0/core.mjs:13043:13) at refreshChildComponents (_karma_webpack_/webp…20/core.mjs:11759:9) at refreshView (_karma_webpack_/webp…0/core.mjs:12019:13) at detectChangesInternal (_karma_webpack_/webp…20/core.mjs:13187:9) at RootViewRef.detectChanges (_karma_webpack_/webp…20/core.mjs:13699:9) at ComponentFixture._tick (_karma_webpack_/webp…/testing.mjs:140:32) at _karma_webpack_/webp…/testing.mjs:153:22
组件构造函数中的Observable定义:
constructor( private store: Store<{ customer: Customer, index: Index}>, ) { this.customer$ = this.store.select(customerSelector); this.index$ = this.store.select(indexSelector); console.log(this.customer$) }
单元测试使用MockStore的代码:
it('should retrieve the answer', () => { store.overrideSelector(customerSelector, { id: 1, name: 'Joe Bradford', gender: 'male' }); store.refreshState(); fixture.detectChanges(); store.select(customerSelector).subscribe((mockTeam) => { expect(mockTeam).toEqual({ id: 1, name: 'Joe Bradford', gender: 'male' }); }) })
注:控制台显示Store为空状态。
解决方案
1. 修复测试代码中的语法错误
测试代码里name: 'Joe'Bradford'存在单引号未转义的语法错误,会导致MockStore无法正确设置初始值。将其修正为'Joe Bradford'(若名字本身不含单引号)或'Joe\'Bradford'(若名字包含单引号)。
2. 提前初始化MockStore状态
当前测试是在组件创建后才覆盖选择器,导致组件初始化时customer$流无数据。需在组件初始化前(比如beforeEach阶段)设置MockStore的初始状态:
beforeEach(() => { TestBed.configureTestingModule({ // ... 其他配置项 providers: [ provideMockStore({ initialState: { customer: { id: 1, name: 'Joe Bradford', gender: 'male' }, index: {} // 根据Index类型设置合理默认值 } }) ] }); store = TestBed.inject(MockStore); // 提前覆盖选择器,确保组件初始化即可拿到数据 store.overrideSelector(customerSelector, { id: 1, name: 'Joe Bradford', gender: 'male' }); }); it('should retrieve the answer', () => { fixture.detectChanges(); // 此时组件已能获取到customer数据 store.select(customerSelector).subscribe((mockCustomer) => { expect(mockCustomer).toEqual({ id: 1, name: 'Joe Bradford', gender: 'male' }); }); });
3. 检查模板中的空值判断逻辑
确保模板中所有访问customer.id的代码都在*ngIf="customer$ | async as customer"之后,避免在customer未初始化时访问其属性:
<!-- 正确写法 --> *ngIf="customer$ | async as customer" <div>{{ customer.id }}</div> <!-- 错误写法(需避免) --> <div>{{ customer.id }}</div> *ngIf="customer$ | async as customer"
4. 验证customerSelector的正确性
检查customerSelector是否正确从Store的customer节点取值,示例如下:
export const customerSelector = createSelector( (state: { customer: Customer }) => state.customer, (customer) => customer );
若选择器路径错误,会导致返回undefined,触发模板错误。
内容的提问来源于stack exchange,提问作者Curtis Lanz
相关产品推荐
相关产品推荐

