NgRx Effects 单元测试:测试 catchError 案例,没有弹珠

NgRx Effects Unit Testing: Test catchError Case, Without Marbles

我正在尝试测试一个简单的场景,定义了以下效果:

import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { EMPTY } from 'rxjs';
import { catchError, map, mergeMap } from 'rxjs/operators';
import { TodosService } from '../services/todos.service';
import { getTodos, getTodosSuccess } from './todos.actions';

@Injectable()
export class TodosEffects {
  loadTodos$ = createEffect(() =>
    this.actions$.pipe(
      ofType(getTodos),
      mergeMap(() => this.todosService.getTodos()
        .pipe(
          map(todos => getTodosSuccess({ todoItems: todos })),
          catchError(() => EMPTY)
        )
      )
    )
  );

  constructor(
    private actions$: Actions,
    private todosService: TodosService
  ) { }
}

在这种情况下,我故意尝试在没有 Marbles 的情况下使它正常工作。也就是说,没有 hot() cold() 等等。我在网上找到的几个关于这个主题的例子只是使用了那些,但我现在正在寻找一种方法来做到这一点。

这是我目前的测试 class。 TodosService 的模拟、快乐路径的首次测试、模拟操作设置等都按预期工作。第二个测试,我基本上是在尝试确认 EMPTY observable 是由 catchError 生成的,这是我正在努力的地方:

import { TestBed } from '@angular/core/testing';
import { TodosService } from '@app/services/todos.service';
import { provideMockActions } from '@ngrx/effects/testing';
import { Action } from '@ngrx/store';
import { EMPTY, Observable, of } from 'rxjs';
import { TodoItem } from './todo-model';
import { getTodos, getTodosSuccess } from './todos.actions';
import { TodosEffects } from './todos.effects';

let actions$ = new Observable<Action>();

let effects: TodosEffects;
let todosServiceSpy: jasmine.SpyObj<TodosService>;

describe('Todos Effects', () => {
  beforeEach(() => {
    const todosServiceSpyObject = jasmine.createSpyObj('TodosService', ['getTodos']);

    TestBed.configureTestingModule({
      providers: [
        TodosEffects,
        provideMockActions(() => actions$),
        { provide: TodosService, useValue: todosServiceSpyObject }
      ]
    });

    effects = TestBed.inject(TodosEffects);
    todosServiceSpy = TestBed.inject<TodosService>(TodosService) as jasmine.SpyObj<TodosService>;
  });

  it('should return successful todo get action with service results', () => {
    actions$ = of(getTodos());
    const expectedTodos: TodoItem[] = [{} as TodoItem];
    todosServiceSpy.getTodos.and.returnValue(of(expectedTodos));
    const expectedAction = getTodosSuccess({ todoItems: expectedTodos });

    effects.loadTodos$.subscribe(result => {
      expect(result).toEqual(expectedAction);
    });
  });

  it('should return EMPTY observable I think?', () => {
    actions$ = of(getTodos());
    // todosServiceSpy.getTodos.and.throwError('');
    todosServiceSpy.getTodos.and.returnValue(throwError(''));
    const expected = EMPTY;

    effects.loadTodos$.subscribe();
    // TODO: What to expect / etc. here?
  });
});

NgRx Effects testing documentation is unfortunately quite lacking here,因为它仅发布示例的片段和边缘案例测试场景。

由于 EMPTY 完成时没有发出下一个或错误,您可以将其用作测试

let nextCount = 0
let errorCount = 0
let completeCount = 0

effects.loadTodos$.subscribe(
  () =>  {nextCount++},
  () =>  {errorCount++}
  () =>  {completeCount++}
)

// test that only completeCount === 1, and others are 0
expect(....).toEqual(...)