Search code examples
angularrxjsangular-unit-test

Observable unit test passing when it should be failing


New to rxjs and angular, attempting to write some basic unit tests for a simple rxjs observable with a success / error scenario:

Service method:

export class MyService {

    constructor(private http: HttpClient) {}

    public getId(id) {
        return this.http.get('www.myurl.com/' + id, {
            withCredential: true,
            responseType: 'json' as 'json'
        })
        .pipe(
            map(response => {
                return response;
            }),
            catchError(() => {
                return 'Service returned error';
            })
        };
    }
}

Test:

it('should throw an error',() => {
    spyOn(httpClientMock, 'get').and.returnValue(Observable.throw('error));

    myService.getId('test1')
        .subscribe(
            () => {},
            error => {
                expect(error).toEqual('Service returned error');
            }
        };
});

The above passes, however, if i change the expect statement to:

expect(error).toEqual('Service returned erro');

The unit test still passes.

If i log out error, i see:

S
e
r
v
i
c
e

r
e
t
u
r
n
e
d

e
r
r
o
r

Solution

  • Seems the issue is that catchError expects an Observable to be returned from the callback fn, not just a value. Try

    import { of } from 'rxjs';
    
    // ...
    
    catchError(() => {
      return of('Service returned error');
    })
    

    see example of it here https://observable-playground.github.io/gist/aa4e24897f4fce150dd92a6fdb0f5929

    UPDATE

    I just realized that your main concern might be particularly passing test. Then the issue probably is in async nature of observables. You need to have a callback in your test to indicate that the test is async and call the callback to mark completion of execution.

    E.g. :

    it('should throw an error',(done) => {
        spyOn(httpClientMock, 'get').and.returnValue(Observable.throw('error));
    
        myService.getId('test1')
            .subscribe(
                () => {},
                error => {
                    expect(error).toEqual('Service returned error');
                },
                // We will call done when Observable completes
                () => done()
            };
    });
    

    Heres an article on Jasmine and async calls:

    https://medium.com/dailyjs/unit-testing-async-calls-and-promises-with-jasmine-a20a5d7f051e