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

如何测试NgRx Store中的值是否已更新?

NgRx MockStore测试:派发动作后Observable未更新的解决办法

问题场景

组件通过updatePreferences方法派发NgRx动作更新用户偏好:

updatePreferences(prefs) {
  this.store.dispatch(patchCurrentUserPreferences({ preferences: prefs }));
}

组件的preferences$是一个Observable,生产环境中订阅能正常收到更新后的偏好值,但在测试环境下,用provideMockStore初始化初始状态后:

provideMockStore({
  initialState: {
    [currentUserFeatureKey]: {
       user: { preferences: { wut: 'lol'} },
    },
  },
})

订阅preferences$只能拿到初始值,派发动作后没有触发更新,即使调用flushMicrotasks或tick也无效。测试代码示例:

it('updates the user preferences', fakeAsync(() => {
  component.preferences$.subscribe((prefs) => {
    console.log('in test world', prefs);
  });
  component.updatePreferences({ some_prop: 'some val' });
  flushMicrotasks();
}));

仅输出初始状态日志,动作派发后无更新。

问题原因

NgRx的MockStore是模拟实现,默认不会像真实Store那样通过reducer自动处理动作并更新状态。它只会维护初始化时设置的状态,除非手动更新状态,或者配置mock reducer来处理动作。

解决办法

办法一:手动更新MockStore状态(简单场景适用)

在派发动作后,手动调用store.setState将Store更新为预期状态,再触发变更检测让Observable收到更新:

import { MockStore, provideMockStore } from '@ngrx/store/testing';
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { YourComponent } from './your.component';
import { currentUserFeatureKey } from './reducers';

describe('YourComponent', () => {
  let component: YourComponent;
  let fixture: ComponentFixture<YourComponent>;
  let store: MockStore;
  const initialState = {
    [currentUserFeatureKey]: {
      user: { preferences: { wut: 'lol' } },
    },
  };

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [YourComponent],
      providers: [provideMockStore({ initialState })],
    }).compileComponents();

    store = TestBed.inject(MockStore);
    fixture = TestBed.createComponent(YourComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('updates the user preferences', fakeAsync(() => {
    const updatedPrefs = { some_prop: 'some val' };
    let receivedPrefs;

    component.preferences$.subscribe(prefs => receivedPrefs = prefs);

    // 验证初始值
    expect(receivedPrefs).toEqual({ wut: 'lol' });

    // 派发动作
    component.updatePreferences(updatedPrefs);

    // 手动模拟reducer处理后的状态更新
    store.setState({
      [currentUserFeatureKey]: {
        user: { preferences: updatedPrefs },
      },
    });

    tick(); // 触发Observable的更新通知
    fixture.detectChanges();

    // 验证更新后的值
    expect(receivedPrefs).toEqual(updatedPrefs);
  }));
});

办法二:配置Mock Reducer(贴近真实场景)

如果希望MockStore自动处理动作,可以编写一个mock reducer并配置到测试模块中,这样派发动作时会自动更新状态:

import { MockStore, provideMockStore } from '@ngrx/store/testing';
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { YourComponent } from './your.component';
import { patchCurrentUserPreferences } from './actions';
import { currentUserFeatureKey, CurrentUserState } from './reducers';

// 编写mock reducer,处理目标动作
const mockCurrentUserReducer = (
  state: CurrentUserState = initialState[currentUserFeatureKey],
  action: any
): CurrentUserState => {
  switch (action.type) {
    case patchCurrentUserPreferences.type:
      return {
        ...state,
        user: {
          ...state.user,
          preferences: action.payload.preferences,
        },
      };
    default:
      return state;
  }
};

describe('YourComponent', () => {
  let component: YourComponent;
  let fixture: ComponentFixture<YourComponent>;
  let store: MockStore;
  const initialState = {
    [currentUserFeatureKey]: {
      user: { preferences: { wut: 'lol' } },
    },
  };

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [YourComponent],
      providers: [
        provideMockStore({
          initialState,
          reducers: {
            [currentUserFeatureKey]: mockCurrentUserReducer,
          },
        }),
      ],
    }).compileComponents();

    store = TestBed.inject(MockStore);
    fixture = TestBed.createComponent(YourComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('updates the user preferences', fakeAsync(() => {
    const updatedPrefs = { some_prop: 'some val' };
    let receivedPrefs;

    component.preferences$.subscribe(prefs => receivedPrefs = prefs);

    expect(receivedPrefs).toEqual({ wut: 'lol' });

    component.updatePreferences(updatedPrefs);

    tick();
    fixture.detectChanges();

    expect(receivedPrefs).toEqual(updatedPrefs);
  }));
});

额外注意点

  • 确保preferences$的select逻辑与MockStore的状态结构完全匹配,否则可能无法正确获取值。
  • 组件测试中记得调用fixture.detectChanges()触发变更检测,确保视图和Observable订阅同步。

内容的提问来源于stack exchange,提问作者patrick

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 12:31:01