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())))))})}
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', () => {});});
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 }) => {});});});
The testScheduler
has a run
function, which ...