如何测试NgRx中的Effects?附getUser Effect实现及优化需求
没问题!我来帮你搞定Angular 5里NgRx getUser Effect的测试。先把你没写完的Effect代码按常规场景补全,然后给你一套完整的测试方案,完全适配你的技术栈~
先补全你的getUser Effect代码(常规业务场景)
假设你的逻辑是:触发GetUser action后调用用户服务获取数据,成功返回GetUserSuccess,失败返回GetUserFailure:
import 'rxjs/add/operator/switchMap'; import 'rxjs/add/operator/catch'; import { Effect, Actions } from '@ngrx/effects'; import { HttpErrorResponse } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { map, switchMap, catchError } from 'rxjs/operators'; import { Observable } from 'rxjs/Observable'; import { of } from 'rxjs/observable/of'; // 假设你的action类型与定义 import { UserActions, UserActionTypes, GetUserSuccess, GetUserFailure } from './user.actions'; import { UserService } from '../services/user.service'; @Injectable() export class UserEffects { @Effect() getUser$: Observable<UserActions> = this.actions$ .ofType<UserActionTypes.GetUser>(UserActionTypes.GetUser) .pipe( switchMap(action => this.userService.getUser(action.payload) .pipe( map(user => new GetUserSuccess(user)), catchError((error: HttpErrorResponse) => of(new GetUserFailure(error.message))) ) ) ); constructor( private actions$: Actions, private userService: UserService ) {} }
完整的测试代码实现
我们用Angular的TestBed配合Jasmine和jasmine-marbles来测试(这个库是NgRx测试的标配,能直观模拟异步流的时间线):
1. 配置测试环境
import { TestBed } from '@angular/core/testing'; import { provideMockActions } from '@ngrx/effects/testing'; import { Observable } from 'rxjs/Observable'; import { cold, hot } from 'jasmine-marbles'; // 导入你的Effect、actions和服务 import { UserEffects } from './user.effects'; import { UserService } from '../services/user.service'; import { UserActions, UserActionTypes, GetUser, GetUserSuccess, GetUserFailure } from './user.actions'; // 创建UserService的Mock类,避免依赖真实后端 class MockUserService { getUser = jasmine.createSpy('getUser').and.returnValue(cold('-a|', { a: { id: 1, name: 'Test User' } })); } describe('UserEffects', () => { let effects: UserEffects; let actions$: Observable<UserActions>; let userService: UserService; beforeEach(() => { TestBed.configureTestingModule({ providers: [ UserEffects, provideMockActions(() => actions$), { provide: UserService, useClass: MockUserService } ] }); effects = TestBed.inject(UserEffects); userService = TestBed.inject(UserService); });
2. 测试成功场景
验证触发GetUser action后,Effect正确调用服务并返回GetUserSuccess:
it('should return GetUserSuccess when fetching user succeeds', () => { const userId = 1; const triggerAction = new GetUser(userId); const successAction = new GetUserSuccess({ id: 1, name: 'Test User' }); // 模拟action流:10ms后触发GetUser actions$ = hot('-a', { a: triggerAction }); // 预期流:20ms后返回GetUserSuccess(对应服务10ms的响应时间) const expected = cold('--b', { b: successAction }); expect(effects.getUser$).toBeObservable(expected); expect(userService.getUser).toHaveBeenCalledWith(userId); });
3. 测试失败场景
验证服务返回错误时,Effect返回GetUserFailure:
it('should return GetUserFailure when fetching user fails', () => { const userId = 1; const errorMsg = 'Failed to load user'; const triggerAction = new GetUser(userId); const failureAction = new GetUserFailure(errorMsg); // 修改Mock服务的返回为错误流 (userService.getUser as jasmine.Spy).and.returnValue(cold('-#|', {}, new Error(errorMsg))); actions$ = hot('-a', { a: triggerAction }); const expected = cold('--b', { b: failureAction }); expect(effects.getUser$).toBeObservable(expected); }); });
适配你的技术栈的小提示
- Angular 5对应的NgRx版本是4/5,
provideMockActions这个API完全兼容 - 如果你的Effect逻辑有差异(比如用了其他操作符、action结构不同),只需要调整测试里对应的action和流模拟部分就行
jasmine-marbles语法说明:-代表10ms时间,a代表一个事件,|代表流结束,#代表错误
内容的提问来源于stack exchange,提问作者AngularM
相关产品推荐
相关产品推荐

