You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 19:45:37