3

I am deperately trying to test a component that is using a service. I use spy to mock it but every time I run tests it fails with exception:

Cannot read property 'subscribe' of undefined

My test looks like this:

describe('FilmOverviewComponent', () => {
  let component: FilmOverviewComponent;
  let fixture: ComponentFixture<FilmOverviewComponent>;

  let filmsServiceSpy: jasmine.SpyObj<FilmsService>;

  beforeEach(async(() => {
    const spy = jasmine.createSpyObj('FilmsService', 
    ['searchFilmByID']
    );

    TestBed.configureTestingModule({
      declarations: [ FilmOverviewComponent ],
      providers: [
        {provide: AppTitleService, useValue: {getTitle: () => 'title'}},
        {provide: ActivatedRoute, useValue: {params: of({id: 123})} },
        {provide: FilmsService, useValue: spy}
      ]
    })
    .compileComponents();

    filmsServiceSpy = TestBed.get(FilmsService);
  }));

  beforeEach(() => {
    filmsServiceSpy.searchFilmByID.and.returnValue(Observable.create([{title: "", year: ""}]));
    fixture = TestBed.createComponent(FilmOverviewComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
   expect(component).toBeTruthy();
  });
});

Methods used in the service:

searchFilmByID(movieID: string): Observable<Film> {
    return this.http.get<Film>(this.getUrlWithID(movieID));
  }

  private getUrlWithID(movieID: string) {
    return 'api/externalfilms/film/' + movieID;
  }

I have no idea how to tackle this. I suspect that it would be resolved with some kind of mocking of subscribe method but I completely failed at that.

Thank you for your help in advance!

1
  • To get rid of this error you can also use 'useClass: MyService' as a parameter for your provider instead of 'useValue'. Commented May 6, 2021 at 8:36

1 Answer 1

1

The error is from this.http.get<Film>.... There are 2 ways to solve it.

First way - mock http client service and call fake

describe('FilmOverviewComponent', () => {
  let component: FilmOverviewComponent;
  let fixture: ComponentFixture<FilmOverviewComponent>;

  let filmsService: FilmsService;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ FilmOverviewComponent ],
      imports: [ HttpClientTestingModule ],
      providers: [
        {provide: AppTitleService, useValue: {getTitle: () => 'title'}},
        {provide: ActivatedRoute, useValue: {params: of({id: 123})} },
        FilmsService
      ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    filmsService = TestBed.inject(FilmsService);

    /* Mock response */
    const httpClient: HttpClient = TestBed.inject(HttpClient);
    spyOn(httpClient, 'get').and.callFake(() => of({title: "", year: ""}));

    fixture = TestBed.createComponent(FilmOverviewComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
   expect(component).toBeTruthy();
  });
});

Second way - call fake service method

const FilmsServiceStub = jasmine.createSpyObj('FilmsService', ['searchFilmByID']);

describe('FilmOverviewComponent', () => {
  let component: FilmOverviewComponent;
  let fixture: ComponentFixture<FilmOverviewComponent>;

  let filmsService: FilmsService;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ FilmOverviewComponent ],
      providers: [
        {provide: AppTitleService, useValue: {getTitle: () => 'title'}},
        {provide: ActivatedRoute, useValue: {params: of({id: 123})} },
        {provide: FilmsService, useValue: FilmsServiceStub}
      ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    filmsService = TestBed.inject(FilmsService);

    /* Mock response */
    spyOn(filmsService, 'searchFilmByID').and.callFake(() => of({title: "", year: ""}));

    fixture = TestBed.createComponent(FilmOverviewComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
   expect(component).toBeTruthy();
  });
});
Sign up to request clarification or add additional context in comments.

1 Comment

Ok, so when I try to use the first way I am unable to put anything inside of() function. It requires of me observable of type ArrayBuffer or Blob. Is it because my interface has a field that contains an array? The second way on the other hand is failing beacuse "searchFilmByID has already been spied upon".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.