question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[jest] "toEqual" timeouts with observe

See original GitHub issue

rxjs-marbles revision: a7ae5dc node: v9.5.0 rxjs: 6.2.0

copy&paste snippet below into ./examples/jest/observe-spec.ts

    it(
        "should property reduce `localState`",
        observe(() => {
            // would be passed as param
            const userState$ = of({
                userName: "jdoe",
                lastLogin: "1231241241"
            });

            const logout$ = never();
            const login$ = never();
            const actionsWithUser$ = merge(
                merge(logout$, login$),
                userState$.pipe(
                    map(userState => localState => ({
                        ...localState,
                        user: userState
                    }))
                )
            );

            const result$ = actionsWithUser$.pipe(
                startWith({ user: undefined, someOtherProp: "foo" }),
                scan((state, reduce) => reduce(state))
            );
            return result$.pipe(
                skip(1),
                tap(val =>
                    expect(val).toEqual({
                        someOtherProp: "foo",
                        user: { lastLogin: "1231241241", userName: "jdoe" }
                    })
                )
            );
        })
    );

when used with toBe there is an proper failure because Object.is equality check

when I use expect(val).toEqual the test timeouts… Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.

EDIT: it also might be related to the skip() call but with scalar state - only number this test works

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:12 (6 by maintainers)

github_iconTop GitHub Comments

2reactions
cartantcommented, Jul 11, 2018

The thing to remember with marble tests is that for the virtual scheduling to work, the root sources have to be observables that were created by the TestScheduler. That is, they have to be created by calling either m.hot or m.cold.

Your test doesn’t work because the subject is not such an observable, so the TestScheduler simply doesn’t know what to do with it - when it comes to virtual scheduling. That is, the call to next won’t fit within the TestScheduler’s concept of virtual time because you’ve called it imperatively - in real time - within the test.

The solution is to use another source to either trigger or map to the subject. See the ReplaySubject tests for an example of the latter. For the former, you could do something like this:

const trigger$ =   m.cold("--t");
const triggerSub =        "^--";
const expected$ =  m.cold("i-u", {
    i: initialState,
    u: updatedState
});
const tapped$ = trigger$.pipe(tap(() => increment(42));
// You'll need an expectation for tapped$ to be subscribed to.
// Either:
m.expect(tapped$).toBeObservable(trigger$);
// Or:
m.expect(tapped$).toHaveSubscriptions(triggerSub);
m.expect(source).toBeObservable(expected$);

It’s likely possible to also achieve this with explicit calls to flush, but I think a declarative approach that lets the TestScheduler do its thing with virtual time is the best way to go.


Also, using m.cold to create the expected observable is just a convenience. Something that I pinched from jasmine-marbles. I find it useful because it lets you specify the expected observable and values alongside the sources. Usually, the toBeObservable takes a marble diagram string and an optional map of values, but calling it that way means that the values are specified separately to the marble diagram (as the marble diagrams are best declared one-after-the-other and aligned).

2reactions
cartantcommented, Jul 1, 2018

It should be pretty easy to test that with a marble test. Something like this should do it:

it(
    "should property reduce `localState`",
    marbles(m => {
        
        const initState = { user: undefined, someOtherProp: "foo" };
        const userState = { userName: "jdoe", lastLogin: "1231241241" };
        const expectedState = { someOtherProp: "foo", user: userState };

        const userState$ = m.cold("--u", { u: userState });
        const logout$ =    m.cold("---");
        const login$ =     m.cold("---");
        const expected$ =  m.cold("i-e", { i: initState, e: expectedState });

        const actionsWithUser$ = merge(
            merge(logout$, login$),
            userState$.pipe(
                map(userState => localState => ({
                    ...localState,
                    user: userState
                }))
            )
        );

        const result$ = actionsWithUser$.pipe(
            startWith(initState),
            scan((state, reduce) => reduce(state))
        );
        m.expect(result$).toBeObservable(expected$);
    });
);

I would recommend attempting to test with a marble test and resort to observe only if writing a marble test proves to be too complicated.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Message "Async callback was not invoked within the 5000 ms ...
Sometimes, when I run the tests, everything works as expectedly. Other times, I get an error: Timeout - Async callback was not invoked...
Read more >
How I divided by ten the execution time of my jest unit testing
Once upon a time, there was a team spending 40 seconds to run their unit tests in local. Exhausted by so much time...
Read more >
Expect - Jest
When you're writing tests, you often need to check that values meet certain conditions. expect gives you access to a number of "matchers" ......
Read more >
Testing RxJS Observables With Jest - Fireship
Learn how to write unit testing RxJS Observables using Jest. ... force completion, like takeWhile, otherwise Jest will timeout after 5000ms.
Read more >
Features - Vitest
Filtering, timeouts, concurrent for suite and tests ... vitest starts in watch mode by default in development environment and run mode in CI ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found