Search code examples
angularunit-testingngrx-storengrx-effectsjasmine-marbles

unit test for a non-dispatching effect


I'm trying to write a unit test for a non-dispatching effect (base on the ngrx guide: https://ngrx.io/guide/effects/testing) But for unknown reasons the effect doesn't seem to catch the action And I don't understand what is wrong with my test.

Effect:

startLocalStorageSync$ = createEffect(() => this.actions$.pipe(
   ofType(START_LOCAL_STORAGE_SYNC),
   switchMap(() => {
       return this.authService.authState$.pipe(
           tap((authState) => {
               this.localStorageService.set(AUTH_COOKIE, authState);
           })
       );
   })
), {dispatch: false});

Unit test:

beforeEach(() => {
   TestBed.configureTestingModule({
       providers: [
        LocalStorageService,
         AuthService,
           AuthEffects,
           provideMockActions(() => actions$)
       ]
   });

   effects = TestBed.inject(AuthEffects);
   authService = TestBed.inject(AuthService);
   localStorageService = TestBed.inject(LocalStorageService);
});

it('should call set to local storage', () => {
   const setSpy : jasmine.Spy = spyOn(localStorageService, 'set');
   actions$ = cold('a', {a: new AuthActions.StartLocalStorageSync()});

   effects.startLocalStorageSync$.subscribe(()=>{
       expect(setSpy).toHaveBeenCalled();
       expect(setSpy).toHaveBeenCalledWith(AUTH_COOKIE, authState);
   });
});

If add the following line, then the effect catches the action and enters the switchmap + the logic in the tap:

expect(effects.initAuthFromLocalStorage$).toBeObservable();

Although I get an error for this line and the actual value of the effect (initAuthFromLocalStorage$) is an object and not an observable.

Thanks!


Solution

  • After reading A non-dispatching Effect in the link you provided, try the following:

    import { of } from 'rxjs';
    ....
    // mock your external dependencies for better control
    let mockLocalStorageService: any;
    let mockAuthService: any;
    beforeEach(() => {
      // create a spyObj with the optional name as first argument and
      // public methods as an array of strings in the second argument
       mockLocalStorageService = jasmine.createSpyObj('localStorageService', ['set']);
      // make auth service into a simple object
       mockAuthService = { authState$: of(true) }; // mock authState$ here
       TestBed.configureTestingModule({
           providers: [
            { provide: LocalStorageService, useValue: mockLocalStorageService },
            { provide: AuthService: useValue: mockAuthService },
               AuthEffects,
               provideMockActions(() => actions$)
           ]
       });
    
       effects = TestBed.inject(AuthEffects);
       authService = TestBed.inject(AuthService);
       localStorageService = TestBed.inject(LocalStorageService);
    });
    
    
    it('should call set to local storage', () => {
       actions$ = of(new AuthActions.StartLocalStorageSync());
    
       effects.startLocalStorageSync$.subscribe();
       // authState is true therefore the true
       expect(mockLocalStorageService.set).toHaveBeenCalledWith(AUTH_COOKIE, true);
    });