Often when writing tests for functions returning RxJS Observables (for example while writing reactive-graphql) I found myself in a situation where I really enjoyed marble testing. It gives you a great way to express expectations around the timings of emitted data and the data itself, so naturally, I write most of my test cases with this framework.
One thing I always struggled with was how to express side-effects of subscribing to an observable in tests. For
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | it("has a side-effect", marbles(m => { const filterFunction = jest.fn(); const outputObservable = myFunction(filterFunction); const expected = m.cold("----x--|", { x: "hello world" }); m.expect(outputObservable).toBeObservable(expected); // Does not work, because these are executed before // m.expect subscribes to the observable expect(filterFunction).toHaveBeenCalledWith("hello"); expect(filterFunction).toHaveBeenCalledWith("world"); })); |
1 2 3 4 5 6 7 8 9 10 11 12 13 | it("has a side-effect", marbles(m => { const filterFunction = jest.fn(); const outputObservable = myFunction(filterFunction); const expected = m.cold("----x--|", { x: "hello world" }); m.expect(outputObservable).toBeObservable(expected); return expected.toPromise().then(() => { expect(filterFunction).toHaveBeenCalledWith("hello"); expect(filterFunction).toHaveBeenCalledWith("world"); }); })); |
With this approach, you don’t get the proper error messages as the assertions are outside of the actual tests and therefore just shown as uncaught errors. To get proper error messages you need to go one step further and use async/await:
1 2 3 4 5 6 7 8 9 10 11 12 | it("has a side-effect", marbles(async m => { const filterFunction = jest.fn(); const outputObservable = myFunction(filterFunction); const expected = m.cold("----x--|", { x: "hello world" }); m.expect(outputObservable).toBeObservable(expected); await expected.toPromise(); expect(filterFunction).toHaveBeenCalledWith("hello"); expect(filterFunction).toHaveBeenCalledWith("world"); })); |
2 comments
Thank you so much. It’s very helpful post. I have been looking for that code for a week at least
.
Thanks a ton, this really means a lot to me!