I am using NgRx and want to test my effects. Some effects do have a debounce time. Like this example:
@Effect() searchImage$ = this.actions$.pipe(
ofType(fromImageLibraryActions.SEARCH_IMAGES),
map((action: fromImageLibraryActions.SearchImages) => action.query),
debounceTime(300),
switchMap(query: string) => this.imageLibraryService.getImagesBySearching(query)),
map((images: LibraryImage[]) => new fromImageLibraryActions.LoadImages(images)));
How do I test them properly. I tried the following:
describe('SearchImages$', () => {
it('should return loadImages action', fakeAsync(() => {
const action = new fromImageLibraryActions.SearchImages('test');
const images = [
{ uploaderId: 1 } as LibraryImage,
{ uploaderId: 2 } as LibraryImage
];
const loadImagesAction = new fromImageLibraryActions.LoadImages(images);
actions$ = hot('--a-', { a: action });
tick(300);
getTestScheduler().flush();
const expected = cold('--b-', { b: loadImagesAction });
expect(effects.searchImage$).toBeObservable(expected);
}));
});
The ngrx examples do not work for me, so I created this video which explains how you can test ngrx effects with debounceTime.
The specific test in on GitHub, here, and looks like this:
import { TestBed } from '@angular/core/testing';
import { provideMockActions } from '@ngrx/effects/testing';
import { cold, getTestScheduler, hot } from 'jasmine-marbles';
import { Observable } from 'rxjs';
import { loadClients, networkRequest } from './client.actions';
import { ClientEffects } from './client.effects';
describe('ClientEffects', () => {
let actions$: Observable<any>;
let effects: ClientEffects;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
ClientEffects,
provideMockActions(() => actions$)
]
});
effects = TestBed.inject<ClientEffects>(ClientEffects);
});
it('dispatch networkRequest', () => {
const scheduler = getTestScheduler();
scheduler.run(() => {
// marbles
const actions = 'a 500ms a b';
const expected = '500ms a - 500ms b';
actions$ = hot(actions, {
a: loadClients(),
b: loadClients(),
});
expect(effects.loadClients$).toBeObservable(cold(expected, {
a: networkRequest(),
b: networkRequest(),
}));
});
});
});
I like to use fake timers and just skip the time, see my blog for more details https://timdeschryver.dev/blog/testing-an-ngrx-project#effect-tests-and-fake-timers.
afterEach(() => {
// don't forget to reset the timers
jest.useRealTimers();
});
it('fetch$ dispatches success action with fake timers', () => {
jest.useFakeTimers();
const actions = new ActionsSubject();
const effects = new WerknemersEffects(actions, getMockStore(), newWerknemerService());
const result: Action[] = [];
effects.fetch$.subscribe((action) => {
result.push(action);
});
const action = werknemerActions.missingWerknemerOpened({ werknemerId: 3 });
actions.next(action);
jest.advanceTimersByTime(10_000);
// 🔦 to make tests less brittle, wait for the task to finish with `runOnlyPendingTimers` or `runOnlyPendingTimers` instead of advancing the time with `advanceTimersByTime`.
// This makes sure that the test isn't impacted when the duration is modified.
jest.runOnlyPendingTimers();
expect(result).toEqual([
werknemerActions.fetchWerknemerSuccess({
werknemer: newWerknemer({ id: action.werknemerId }),
}),
]);
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With