4

I have the following 2 components and a single service that is shared by both. I need to unit test them but I'm unable to figure out how to test the dependency of the service on the following components.

//a.component.ts
import { Component, Input } from '@angular/core';
import { Http, Response } from '@angular/http';
import { SharedService } from './shared/shared.service';

@Component({
  selector: 'a',
  providers: [],
  templateUrl: './a.component.html'
})
export class AComponent {
  ax = {};

  constructor(public helperService: SharedService) {
    helperService.getFromAPI().subscribe(data => this.ax = data["people"]);
  }

}



//b.component.ts
import { Component } from '@angular/core';
import { SharedService } from './shared/shared.service';
import { Subscription }   from 'rxjs/Subscription';


@Component({
  selector: 'b',
  providers: [],
  templateUrl: './b.component.html'
})

export class BComponent {
  subscription: Subscription;
  x = '';

  constructor(public helperService: SharedService) {}

  ngOnInit() {
    this.subscription = this.helperService.c$.subscribe(
      data => {
        this.x = data;
      });
  }
}

This is the service that calls the API. The other function setC add the value to the observable upon a button click and this value is to be accessed by the BComponent.

// shared.service
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/map';

@Injectable()
export class SharedService {

  private c = new Subject<string>();
  c$ = this.c.asObservable();

  constructor(
    private http: Http
  ) { }

  getFromAPI() {
    return this.http.get('url')
      .map((res: Response) => res.json());
  }

  setC(data: string) {
    this.c.next(data);
  }
}

How can I test this in Jasmine? So far, my efforts have been futile.

I've tried doing like

it('xxx', inject([SharedService], (service: SharedService) => {
    const fixture = TestBed.createComponent(AComponent);
    const app = fixture.componentInstance;

    spyOn(service, 'c$').and.callThrough;

    service.setC('Random Name');
    expect(service.c$).toHaveBeenCalled();

  }));

This fails the test with Expected spy c$ to have been called..

Sadar Ali
  • 153
  • 1
  • 6

1 Answers1

3

You're spying on an Observable it seems but what's called when you call setC is the next function of your Subject. So you probably want to spy on that instead.

Something like spyOn(service.c, 'next').and.callThrough() should do the trick.

Hope it helps.


Update: If you want explicitely to test the functionality of your Observable then i would just subscribe to it, call setC and test the response, something like this:

service.$c.subscribe((data) => {
    expect(data).toBe('foobar');
});
service.setC('foobar');

To answer your question in the comment: Since your c is private you can spy on it like this: spyOn(service['c'], 'next').and.callThrough(). It could be that your ide will nag about spying on private methods, in this case you can add the any type like this: spyOn<any>(...)

malifa
  • 8,025
  • 2
  • 42
  • 57
  • But `c` is `private`. How can I spy on that? – Sadar Ali Jul 29 '17 at 22:40
  • @SadarAli It doesn't answer your question per se but [this question](https://stackoverflow.com/questions/12713659/typescript-private-members) might be helpful – 0mpurdy Jul 29 '17 at 22:44
  • @lexith This trick works if I remove `c` as private. How should I proceed if I want to mock the `getFromAPI` call (with some json) using a similar kind of spy? – Sadar Ali Jul 29 '17 at 22:48
  • Do you mean how you would test `getFromApi`? Yes use a spy on the `get` method of `http` like `spyOn(service['http'], 'get');` Within your component you probably would want to test the response though, which is another question (then you would have to mock http using `MockBackend`) – malifa Jul 29 '17 at 22:53