1

I'm trying to correctly unsubscribing Observable in my Angular application using takeUntil() but it seems to break the test.

Chrome 58.0.3029 (Mac OS X 10.12.5) AppComponent should render title in a h1 tag FAILED
Failed: this.appService.getGuides(...).takeUntil is not a function
TypeError: this.appService.getGuides(...).takeUntil is not a function
    at AppComponent.Array.concat.AppComponent.ngOnInit (webpack:///src/app/app.component.ts:28:7 <- src/test.ts:53764:14)
    at checkAndUpdateDirectiveInline (webpack:///~/@angular/core/@angular/core.es5.js:10923:0 <- src/test.ts:11228:19)

This is code:

  export class AppComponent implements OnDestroy, OnInit {
  private ngUnsubscribe: Subject<void> = new Subject<void>();

  title = 'app works!';
  guides: Guide[];

  constructor(private appService: AppService) {
  }

  ngOnInit(): void {
    this.appService.getGuides()
      .takeUntil(this.ngUnsubscribe)
      .subscribe(
        guides => this.setGuides(guides),
        error => this.onError(error)
      );
  }

  ngOnDestroy(): void {
    this.ngUnsubscribe.next();
    this.ngUnsubscribe.complete();
  }

This is the service implementation:

@Injectable()
export class AppService {

  constructor(private http: Http) { }

  getGuides(): Observable<Guide[]> {
    return this.http.get(environment.apiUrl + '/guides')
      .map(this.extractData);
  }

  private extractData(res) {
    if (res.status < 200 || res.status >= 300) {
      throw new Error('Bad response status: ' + res.status);
    }
    return res.json();
  }
}

And this is the test that is failing:

class MockAppService extends AbstractMockObservableService {
  getGuides() {
    return this;
  }
}
describe('AppComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [AppComponent]
    });

    TestBed.overrideComponent(AppComponent, {
      set: {
        providers: [
          { provide: AppService, useClass: MockAppService }
        ]
      }
    });
  }));
  it('should render title in a h1 tag', async(() => {
    const fixture = TestBed.createComponent(AppComponent);
    fixture.detectChanges();
    const compiled = fixture.debugElement.nativeElement;
    expect(compiled.querySelector('h1').textContent).toContain('app works!');
  }));
Cedric
  • 51
  • 2
  • 4
  • 1
    post this.appService.getGuides() code? – CharanRoot Jun 19 '17 at 19:27
  • I just added the services implementation as well as the mock service that I use in the test, following [this](https://stackoverflow.com/questions/39960146/angular-2-testing-error-case-with-observables-in-services/39960878#39960878) – Cedric Jun 20 '17 at 17:16

3 Answers3

1

Try this:

class MockAppService extends AbstractMockObservableService {
  getGuides() {
    return Observable.of([]);
  }
}

I just had this issue myself. In my case I was using a spy like this:

spy = spyOn(someService, 'get')
    .and.returnValue([]);

What I needed to do was do this:

spy = spyOn(someService, 'get')
    .and.returnValue(Observable.of([]));

There is no takeUntil method for your mock implementation because it's not returning an observable (unlike the real implementation).

cookavich
  • 118
  • 9
0

You need to import it in your spec file:

import 'rxjs/add/operator/takeUntil';
Meir
  • 14,081
  • 4
  • 39
  • 47
0

getGuides() in your MockAppService shouldn't return this, but an observable.

beetstra
  • 7,942
  • 5
  • 40
  • 44