如何测试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
相关产品推荐
相关产品推荐

