如何对NgRx Effects中withLatestFrom操作符进行单元测试
看起来你的测试里有几个关键问题导致了这个错误,我来帮你逐个梳理并修正:
1. 服务方法返回值不是Observable
你的boardService.getBoardsFromParams被mock成直接返回普通对象successfulSignalRResult,但在实际Effect代码中,这个方法应该返回一个Observable(因为你调用了.pipe()操作符)。RxJS的操作符需要处理流,所以必须让mock方法返回Observable:
boardSvc.getBoardsFromParams = jest.fn(() => of(successfulSignalRResult));
2. Store选择器的Mock方式问题
你当前的store.select mock虽然返回了Observable,但需要确保它匹配你在Effect中使用的选择器fromRoot.getRouterState。另外,NgRx的store.select通常是通过选择器来调用的,最好明确mock对应选择器的返回:
// 方式1:mock指定的选择器 jest.spyOn(fromRoot, 'getRouterState').mockReturnValue(of(params)); // 方式2:如果直接mock store.select方法 store.select = jest.fn((selector) => { if (selector === fromRoot.getRouterState) { return of(params); } return of(null); });
另外,你传入的params结构要和Effect中解构的一致,确保router.state.queryParams.boards能正确取到值(这里你传的是空对象,所以boards会是undefined,这没问题,但结构要对)。
3. Action类型不匹配
你的Effect中成功后返回的是backlogActions.GetBoardsSuccess(board),但测试里的预期结果是new backlogActions.GetAvailableBoardsSuccess([]),这两个action类型不匹配,会导致测试断言失败。要把预期的action改成和Effect中一致的类型:
const outcome = new backlogActions.GetBoardsSuccess([]);
4. toBeObservable的参数错误
你最后调用expect(effects.loadPlansOnParams$).toBeObservable({});,这里应该传入你定义的expected变量,而不是空对象:
expect(effects.loadPlansOnParams$).toBeObservable(expected);
修正后的完整测试代码
describe('getting boards from the params', () => { it('should return an empty array if no params', () => { const params = { state: { queryParams: {} } }; const successfulSignalRResult = { isSuccessful: true, item: [] }; const action = new backlogActions.GetBoardsInParams(); // 修正:使用和Effect中一致的Success action const outcome = new backlogActions.GetBoardsSuccess([]); actions.stream = hot('-a', { a: action }); // 修正:正确mock store.select返回对应选择器的Observable jest.spyOn(fromRoot, 'getRouterState').mockReturnValue(of(params)); expected = cold('--b', { b: outcome }); // 修正:让服务方法返回Observable boardSvc.getBoardsFromParams = jest.fn(() => of(successfulSignalRResult)); // 修正:传入expected而不是空对象 expect(effects.loadPlansOnParams$).toBeObservable(expected); }); });
另外,还要确保你在测试前正确初始化了Effects、Actions、Store和BoardService的mock实例,比如:
let effects: YourEffectsClass; let actions: Actions; let store: MockStore; let boardSvc: jest.Mocked<BoardService>; beforeEach(() => { TestBed.configureTestingModule({ providers: [ YourEffectsClass, { provide: Actions, useValue: new Actions(EMPTY) }, { provide: BoardService, useValue: { getBoardsFromParams: jest.fn() } }, provideMockStore(), ], }); effects = TestBed.inject(YourEffectsClass); actions = TestBed.inject(Actions); store = TestBed.inject(MockStore); boardSvc = TestBed.inject(BoardService) as jest.Mocked<BoardService>; });
这些修改应该能解决你遇到的"undefined stream"错误,同时让测试正确运行。
内容的提问来源于stack exchange,提问作者s.Lawrenz

