Search code examples
typescriptts-jestrxjs-observables

Jest test fails with Observable emitting an error


I noticed that when using rxjs' Observables with Jest, I have tests failing if I wait for a bit, but passing if I do not. If an Observable is subscribed to and emits an error, the test will fail as if the error was thrown directly by the subscription. It does not seem to be preventable by wrapping the subscription in a try/catch block:

import { throwError } from 'rxjs';

describe('Unhandled Promise rejection', () => {
    it('fails when waiting for a bit', (done) => {
        const error = new Error('Error waiting for 1000');
        const obs$ = throwError(() => error);

        try {
            obs$.subscribe();
        } catch (e) {
            console.warn('The error was emitted: %s', e);
        }

        setTimeout(done, 1000);
    });

    it('fails when waiting for 0ms', (done) => {
        const error = new Error('Error waiting for 0');
        const obs$ = throwError(() => error);

        try {
            obs$.subscribe();
        } catch (e) {
            console.warn('The error was emitted: %s', e);
        }

        setTimeout(done, 0);
    });

    it('passes when not waiting', () => {
        const error = new Error('Error not waiting');
        const obs$ = throwError(() => error);

        try {
            obs$.subscribe();
        } catch (e) {
            console.warn('The error was emitted: %s', e);
        }
    });
});

/*
 * Error: Error waiting for 1000
 * 
 *     at test.spec.ts:5:17
 * 
 * Error: Error waiting for 0
 * 
 *     at test.spec.ts:18:17
 */

The first two tests fail without printing any warning to the console, whereas the third test passes without an issue.

I saw that there is an issue with unhandled rejecting promises in Jest: https://github.com/jestjs/jest/issues/9210. However, even if I thought that it was related at first, I managed to isolate the issue with Observables only.

How can I fix this so that the tests pass if I wait for them to finish? I am using rxjs 7.8.0 and Jest 29.5.0.


Solution

  • It appears that Jest does not like unhandled errors and exceptions, as hinted at in the GitHub issue linked in the post. However, it seems to be a problem of interaction between Jest and Observables here, but I do not know where the problem stems from.

    While a definitive answer or fix for this would be greatly appreciated, a workaround for now is to simply add a dummy error handler in the subscription:

    import { throwError } from 'rxjs';
    
    describe('Ignoring errors seems to do the trick', () => {
        it('passes when waiting for a bit', (done) => {
            const error = new Error('Error waiting for 1000');
            const obs$ = throwError(() => error);
    
            obs$.subscribe({ error: () => {} });
    
            setTimeout(done, 1000);
        });
    
        it('passes when waiting for 0ms', (done) => {
            const error = new Error('Error waiting for 0');
            const obs$ = throwError(() => error);
    
            obs$.subscribe({ error: () => {} });
    
            setTimeout(done, 0);
        });
    
        it('passes when not awaited', () => {
            const error = new Error('Error not waiting');
            const obs$ = throwError(() => error);
    
            obs$.subscribe();
        });
    });