DEV Community

Cover image for Testing NGRX effects without testbed
Will Poulson
Will Poulson

Posted on • Originally published at willpoulson.co.uk on

Testing NGRX effects without testbed

The benefits

When testing NGRX effects the testbed is not needed. Dependencies can be mocked and directly passed to the constructor and actions can just be a simple observable stream of any form.

The testbed often creates additional bloat slowing down your unit tests and requiring more boilerplate to be written when mocking injection dependencies.

How do you mock actions without the testbed?

It's very simple. First remove the testbed configureTestingModule and inject code and replace with a raw constructor.

describe('ExampleEffects', () => {
    ...
    let effects: ExampleEffects;

    beforeEach(() => {
        effects = new ExampleEffects();
    });

    ...
});
Enter fullscreen mode Exit fullscreen mode

Then we need to create a new observable stream for our action as we can't use ngrx'z included provideMockActions anymore.
I like to use a replay subject for this as it allows for jest-marbles tests to run successfully.

describe('ExampleEffects', () => {
    ...
    let effects: ExampleEffects;
    let actions$: ReplaySubject<Action>;

    beforeEach(() => {
        actions$ = new ReplaySubject<Action>();
        effects = new ExampleEffects(actions$);
    });

    ...
});
Enter fullscreen mode Exit fullscreen mode

Now to dispatch actions all we need to do is call next on our replay subject with the relevant action.

Let's see this in action. Below is an effect file which contains an effect that listens for actionA and then spits out actionB.

export class ExampleEffects {
    exampleEffect$ = createEffect(() =>
        this.actions$.pipe(
            ofType(ExampleActions.actionA),
            map(() => ExampleActions.actionB())
        )
    );

    constructor(private actions$: Actions) {}
}
Enter fullscreen mode Exit fullscreen mode

And below is my spec file.

describe('ExampleEffects', () => {
    let effects: ExampleEffects;
    let actions$: ReplaySubject<Action>;

    beforeEach(() => {
        actions$ = new ReplaySubject<Action>();
        effects = new ExampleEffects(actions$);
    });

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

    describe('exampleEffect$', () => {
        beforeEach(() => actions$.next(ExampleActions.actionA()));

        it('should return the deleteUserData action', () => {
            const expected$ = cold('a', { a: ExampleActions.actionB() });

            expect(effects.exampleEffect$).toBeObservable(expected$);
        });
    });
});
Enter fullscreen mode Exit fullscreen mode

Conclusion

I hope someone finds this useful, removing the testbed is surprisingly simple for NGRX effects and you may notice considerable performance improvements in large effects files.

Top comments (0)