...

/

Testing Effects: Testing the Success Case

Testing Effects: Testing the Success Case

Learn how to write unit testing code to handle the success case of the effects.

Introduction

The ProductsEffects have been defined so that, when the loadProductsAction is dispatched from our application, the loadProducts$ effect detects it by subscribing to the actions$ observable in lines 17–18. Then, in order to fetch the product data, our effects connect to the server in line 19. If the product data is successfully returned from the server, we have dispatched another action called getProductsAction() in line 20.

Let’s unit test this success case in this lesson.

import { Injectable } from "@angular/core";
import { Actions, createEffect, ofType } from "@ngrx/effects";
import { catchError, map, mergeMap, of } from "rxjs";
import { Product } from "src/app/app.interfaces";
import { ProductsService } from "src/app/products.service";
import { loadProductsAction, getProductsAction, getErrorAction } from "./products.actions";
@Injectable()
export class ProductsEffects {
constructor(
private actions$: Actions,
private productsService: ProductsService
) { }
loadProducts$ = createEffect(() => {
return this.actions$.pipe(
ofType(loadProductsAction),
mergeMap(action => this.productsService.getProducts().pipe(
map((products: Product[]) => getProductsAction({ products })),
catchError(() => of(getErrorAction()))
))
)
})
}
ProductsEffects successfully fetching data from the server

Let’s visualize the success case in the following illustration:

Unit testing the success case

We can unit test the success case by following the steps below.

Defining the specs

Let’s use the it function to define the specs of our test in lines 32–34.

import { TestBed } from '@angular/core/testing';
import { Actions } from '@ngrx/effects';
import { provideMockActions } from '@ngrx/effects/testing';
import { TestScheduler } from 'rxjs/testing';
import { ProductsService } from 'src/app/products.service';
import { ProductsEffects } from './products.effects';
describe('ProductsEffects: loadProducts$', () => {
let effects: ProductsEffects;
let actions$: Actions;
let productsServiceSpy = jasmine.createSpyObj('ProductsService', [
'getProducts',
]);
let testScheduler: TestScheduler;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
ProductsEffects,
provideMockActions(() => actions$),
{ provide: ProductsService, useValue: productsServiceSpy },
],
});
effects = TestBed.inject(ProductsEffects);
testScheduler = new TestScheduler((actual, expected) => {
expect(actual).toEqual(expected);
});
});
it('should handle loadProductsAction and return getProductsAction on success', () => {
});
});
Defining the specs

Executing the testScheduler

In the ProductsEffects, we have interacted asynchronously with the server via the ProductsService to fetch the data. To test this asynchronous behavior, let’s use the testScheduler in lines 34–36.

import { TestBed } from '@angular/core/testing';
import { Actions } from '@ngrx/effects';
import { provideMockActions } from '@ngrx/effects/testing';
import { TestScheduler } from 'rxjs/testing';
import { ProductsService } from 'src/app/products.service';
import { ProductsEffects } from './products.effects';
describe('ProductsEffects: loadProducts$', () => {
let effects: ProductsEffects;
let actions$: Actions;
let productsServiceSpy = jasmine.createSpyObj('ProductsService', [
'getProducts',
]);
let testScheduler: TestScheduler;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
ProductsEffects,
provideMockActions(() => actions$),
{ provide: ProductsService, useValue: productsServiceSpy },
],
});
effects = TestBed.inject(ProductsEffects);
testScheduler = new TestScheduler((actual, expected) => {
expect(actual).toEqual(expected);
});
});
it('should handle loadProductsAction and return getProductsAction on success', () => {
testScheduler.run(({ hot, cold, expectObservable }) => {
});
});
});
Executing the testScheduler

The testScheduler has a run function, which ...