Mohamed Sahir
Mohamed Sahir

Reputation: 2543

RXjs 6 callbacks testing with jasmine

Here is a simple post function, I am able to unit test success and catchError in jasmine. Is it possible to test the finalize in jasmine? i.e in finalize, can we expect loader to be closed or not?

 post(url,requestData){
    this.http.post(url, requestData).pipe(
          response => this.Response(response),
          catchError((error: Response) => this.Error(error, msg)),
          finalize(() => {
            loader.close();
          })
    }

In finalize I am closing loader. I need to unit test the close loader to be called in finalize.

Upvotes: 1

Views: 1510

Answers (2)

Chris
Chris

Reputation: 73

I had a case where I wanted to ensure that a "remove loading indicator" method was called from a finalize block of an Observable. The service is a home-grown one and not HTTP, but it's returning an observable so hopefully you can adapt it. The keys that I found are: 1) The test has to run in a fakeAsync() block. 2) I had to call next on the (mocked) subscription. 3) I then had to call complete() on the observable.

So, my solution was basically:

it ("should do something in finalize", () => {
  let fakeResult = new Subject<any>();

  // the "stop" method is called in the finalize of the Observable returned from testMethod()
  spyOn(loadingService, "stop");  

  spyOn(service, "testMethod").andReturn(fakeResult);
  component.methodUnderTestThatCallsServiceTestMethod(); // Observable returned here
  fakeResult.next("whatever");  // Observable subscription fired here
  tick();
  fakeResult.complete();  // Observable finalize triggered here
  expect(loadingService.stop).toHaveBeenCalled();
});

Upvotes: 1

Lucas Lacerda
Lucas Lacerda

Reputation: 11

I've struggling with the same problem. In my case i was trying to test the interceptor in Angular.

My solution was pass a real Observable as mock.

This is my actual code:

@Injectable()
export class SetLoadingService implements HttpInterceptor {

  constructor(@Inject('INotificationLoading') private _INotificationLoading: INotificationLoading) {}

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (req.headers.has('HideLoading')) {
      return next.handle(req);
    } else {
      this._INotificationLoading.openLoading();
      return next.handle(req).pipe(finalize(() => {
        this._INotificationLoading.closeLoading();
      }));
    }
  }
}

And this is my test:

it('should be show loading', () => {
    const next: any = {
      handle: () => {
        return Observable.create(subscriber => {
          subscriber.complete();
        });
      }
    };
    const requestMock = new HttpRequest('GET', '/test');
    const closeLoadingSpy = spyOn<INotificationLoading>(notification, 'closeLoading');

    service.intercept(requestMock, next).subscribe(() => {
      expect(closeLoadingSpy).toHaveBeenCalled();
    });

    expect(openLoadingSpy).toHaveBeenCalled();

  });

In a nutshell i solve the problem passing a Observable.create() as the return of the method handle. In your case you can set the return of http mock an Observable and set the expectation you need to check the test.

Upvotes: 1

Related Questions