Mj1992
Mj1992

Reputation: 3504

Angular 7 Unit testing Observable subscribe call

I've a GeoLocationService in my angular application like this.

import { Injectable } from "@angular/core";
import { Observable } from "rxjs/Observable";

@Injectable()
export class GeoLocationService {
  coordinates: any;

  constructor() {}

  public getPosition(): Observable<Position> {
    return Observable.create(observer => {
      navigator.geolocation.watchPosition((pos: Position) => {
        observer.next(pos);
      }),
        () => {
          console.log("Position is not available");
        },
        {
          enableHighAccuracy: true
        };
    });
  }
}

I want to unit test this service to ensure that the getPosition() function returns me a working Observable. Here's how my test looks like.

import { TestBed, fakeAsync } from "@angular/core/testing";

import { GeoLocationService } from "./geo-location.service";
import { take } from "rxjs/operators";

describe("GeoLocationService", () => {
  let service: GeoLocationService;
  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [GeoLocationService]
    });
    service = TestBed.get(GeoLocationService);
  });


  it("should get observable value", fakeAsync((done: DoneFn) => {
    service
      .getPosition()
      .subscribe(value => {
        expect(value.coords).not.toBeUndefined();
        expect(value.coords).not.toBeNaN();
        expect(value.coords).not.toBeNull();
        done();
      });
  }));
});

What happens here is that once I run the test the test is successful ( this is because the subscribe block inside the test is not run yet). On the chrome browser window that opens ( for viewing test results in karma ), I select Allow for the application to find my location, it is at this moment the subscribe block runs but by that time the it spec has already finished due to this reason I get a console error saying.

Uncaught Error: 'expect' was used when there was no current spec, this could be because an asynchronous test timed out.

I don't know what is the recommended way to perform this test.

Upvotes: 4

Views: 9130

Answers (2)

Mj1992
Mj1992

Reputation: 3504

I was able to get it working using the following code.

  it("should get current user coordinates", (done: DoneFn) => {
    let position: any;
    service.getPosition().subscribe(value => {
      position = value;
      expect(position.coords).not.toBeUndefined();
      expect(position.coords).not.toBeNaN();
      expect(position.coords).not.toBeNull();
      done();
    });
  });

For this the test waits until you Allow the browser window to get your location. If you wait for too long, it will throw a timeOut error. Good enough for me to test what I wanted.

Upvotes: 5

devnull69
devnull69

Reputation: 16574

You can use tick() which fakes the test into being synchronous-ish :-)

  it("should get observable value", fakeAsync((done: DoneFn) => {
    let matchValue = {coords: null};
    service
      .getPosition()
      .subscribe(value => {
        matchValue = value;
      });
      tick(2000); // whatever fits your needs
      expect(matchValue.coords).not.toBeUndefined();
      expect(matchValue.coords).not.toBeNaN();
      expect(matchValue.coords).not.toBeNull();
      done();
  }));

Upvotes: 1

Related Questions