Search code examples
ngrxngrx-effects

How to test NgRx Effect that uses event from dependency injected service


I have following effect that uses the an observable source from a PlatformService provided by Angulars Dependency Injection.

public resume$ = createEffect(() => {
  return this.platformService.resume().pipe(mapTo(PlatformActions.appResumed()));
});

constructor(private platformService: PlatformService) {
}

This works really well, but I do not really know how to test it elegantly. Usually we try to setup our TestingModule as unspecific as possible. e.G

describe('PlatformEffects', () => {
  let effects: PlatformEffects;
  let platformServiceSpy: SpyObj<PlatformService>;

  beforeEach(() => {
    platformServiceSpy = jasmine.createSpyObj({
      resume: EMPTY
    });

    TestBed.configureTestingModule({
      providers: [
        PlatformEffects,
        {
          provide: PlatformService,
          useValue: platformServiceSpy
        }
      ]
    });

    effects = TestBed.inject(PlatformEffects);
  });

  it('should be created', () => {
    expect(effects).toBeTruthy();
  });

  describe('resume$', () => {
    it('dispatches appResumed action on resume event', (done) => {
      // AAA: This test should setup the response from the platformService so the arrange setup is coupled in the test.
    });
  });
});

Sadly this does not seem to work as the effects are created in the constructor and therefore my event stream is always EMPTY and does not trigger the effect even if I overwrite the resume response in my spy.

Now I could set a default value in the beforeEach (e.G. of(undefined)) but then it is not really coupled in my tests and I cant use the AAA pattern.

On the other hand I could probably create a new Effects instance every time but that seems a bit overkill not?

Is there a better method? NgRx solved it really well with the actions stream and I wonder if there is a similar solution for effects using sources from DI.


Solution

  • For more info see, https://timdeschryver.dev/blog/testing-an-ngrx-project#effects

    
    it('fetch$ dispatches a success action', () => {
        // 🔦 The Effect Actions stream is created by instantiating a new `ActionsSubject`
        const actions = new ActionsSubject();
        const effects = new CustomersEffects(actions, newCustomerService());
    
        // 🔦 Subscribe on the effect to catch emitted actions, which are used to assert the effect output
        const result: Action[] = [];
        effects.fetch$.subscribe((action) => {
            result.push(action);
        });
    
        const action = customerPageActions.enter({ customerId: '3' });
        actions.next(action);
    
        expect(result).toEqual([
            customersApiActions.fetchCustomerSuccess(
                newCustomer({
                    id: action.customerId,
                }),
            ),
        ]);
    });