Reputation: 3899
I'm trying to test a simple scenario, with the following Effect defined:
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
) { }
}
In this scenario, I am deliberately trying to get this working without Marbles. That is, without hot()
cold()
and so on. The few examples I've found online covering this topic just use those, but I'm looking for a way to do this without that for now.
Here's what I have in my test class so far. The mocking of TodosService
, first test of the happy path, mock actions setup, etc. all work as expected. The second test, the one where I'm basically trying to confirm that the EMPTY
observable is generated by catchError
, is where I'm struggling:
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?
});
});
The NgRx Effects testing documentation is unfortunately quite lacking here, as it only posts snippets and edge case testing scenarios for examples.
Upvotes: 0
Views: 1320
Reputation: 12377
Since EMPTY
completes without emitting next or error, you could use that as the test
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(...)
Upvotes: 1