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

如何测试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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 08:22:35