1

I am currently writing a unit test for Geolocation API.

My Angular component looks as follow:

export class HeaderComponent {
  public lat: number = 56.713;
  public lng: number = 21.1644;
  public message: string;
  public messageType: string;

  public locationSuccess(data: any) {
    console.log(data);
    if (data) {
      if (data.coords) {
        this.lat = data.coords.latitude ? data.coords.latitude : this.lat;
        this.lng = data.coords.longitude ? data.coords.longitude : this.lng;
        this.messageType = 'success';
        this.message = 'You successfully granted us retrieving your location to ' +
                       'enhance your experience.';
      }
    }
  }

  public locationError() {
    console.log('error');
    this.message = 'Unfortunately we could not acquire your location which is recommended ' +
                   'for best user experience with our service.';
    this.messageType = 'danger';
  }

  public enableNavigatorLocation() {
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(
        this.locationSuccess.bind(this),
        this.locationError.bind(this)
      );
    }
  }
}

And my unit test looks like this:

// synchronous beforeEach
beforeEach(() => {
  fixture = TestBed.createComponent(HeaderComponent);
  comp = fixture.componentInstance;
});

it('enableNavigatorLocation should call locationSuccess if successful', () => {
  const locationSuccess = jasmine.createSpy('locationSuccess');
  const locationError = jasmine.createSpy('locationError');

  spyOn(navigator.geolocation,'getCurrentPosition').and.callFake(function(locationSuccess, locationError) {
    const position = { coords: { latitude: 32, longitude: -96 } };
    arguments[0](position);
  });

  comp.enableNavigatorLocation();

  expect(locationSuccess).toHaveBeenCalled();
});

My spy is not called and I have no idea what I am doing wrong. Could there be a problem with the function call via bind() in enableNavigatorLocation method?

I used this post from 2012 as a guideline

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
Michael Andorfer
  • 1,660
  • 5
  • 25
  • 45
  • Is navigator declared like so `declare var navigator: any;` ? –  May 24 '17 at 09:50
  • @trichetriche where has this line of code to go? directly after the import statements? – Michael Andorfer May 24 '17 at 09:54
  • No, I mean, you don't declare your navigator variable by `let navigator = 'something';` so I'm, asking, is that a global variable declared with `declare var` in your component ? –  May 24 '17 at 09:55
  • yes it is globally declared as `declare var navigator: Navigator;` in lib.es6.d.ts – Michael Andorfer May 24 '17 at 09:58
  • then take a look at [this topic](https://stackoverflow.com/questions/41099179/test-component-with-declare-var) which explains how to handle those cases. –  May 24 '17 at 10:00
  • @trichetriche I do not think that this is the problem because it has access to navigator and its functions – Michael Andorfer May 24 '17 at 12:14

1 Answers1

1

This happens because spying went wrong.

locationSuccess and locationError spies are local variables. They are never used. The fact that params in callFake have same names doesn't affect anything.

The proper way to do this is to stub navigator.geolocation.getCurrentPosition:

spyOn(navigator.geolocation, 'getCurrentPosition');

comp.enableNavigatorLocation();

expect(navigator.geolocation.getCurrentPosition).toHaveBeenCalledWith(
  comp.locationSuccess,
  comp.locationError
);
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • this throws `Expected spy getCurrentPosition to have been called with [ Function, Function ] but actual calls were [ Function, Function ]`. – Michael Andorfer May 24 '17 at 12:16
  • and what is a proper way to test success and error then? – Michael Andorfer May 24 '17 at 12:18
  • 1
    I see. The proper place to bind callback methods is class constructor, `this.locationSuccess = this.locationSuccess.bind(this)`. Not the place where they are called. You should test them in a separate test by calling them and putting `expect`s on component properties that they are supposed to set. – Estus Flask May 24 '17 at 12:31