简体   繁体   中英

Unit test for a subscribe method in a component with Jasmine/Karma

I want to test method with subscribe which invoked by ngOnInit in component. This method called getPagesJson, defined in getjsonService and gets one value from JSON file.

Goal of the my test - put some value to the getPagesJson with a stub and will compare it with original data by testing process.

The subscribe unit test doesn't work. Code below:

first-block.component.ts

import { Component, OnInit } from '@angular/core';
import { GetJsonService } from '../../services/get-json.service';

@Component({
  selector: 'app-first-block',
  template: '<h1 class="page-title">{{h1}}</h1>',
  styleUrls: ['./first-block.component.css']  
})

export class FirstBlockComponent implements OnInit {

  h1:any;

  constructor(private getjsonService: GetJsonService) { }

  ngOnInit() {
    this.getjsonService.getPagesJson()
    .subscribe(
      data => this.h1=data["index"][0]["h1"],
      error => console.log(error)
    );
  }
}

get-json.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { HttpErrorResponse } from '@angular/common/http';
import { throwError } from 'rxjs';
import { catchError, retry } from 'rxjs/operators';

import { PagesInt } from '../interfaces/pages-int';

@Injectable({
  providedIn: 'root'
})
export class GetJsonService {

    // Error handler
    private handleError(error: HttpErrorResponse) {
      if (error.error instanceof ErrorEvent) {
        console.error('Error message:', error.error.message);
      } else {  
        console.error(
          `Code from backend ${error.status}, ` +
          `Error: ${error.error}`);
      }
      return throwError(
        'Something is wrong');
    };

    constructor(private http: HttpClient) { }

    getPagesJson() {
      return this.http.get<PagesInt>('/assets/from-server/pages.json')
      .pipe( 
        retry(3), 
        catchError(this.handleError)
      );    
    }     
}

pages-int.ts

export interface PagesInt {
    h1:string;
    videoUrl?:string;
}

first-block.component.spec.ts

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { of, Observable } from "rxjs";

import { FirstBlockComponent } from './first-block.component';
import { GetJsonService } from '../../services/get-json.service';

describe('FirstBlockComponent', () => {
  let component: FirstBlockComponent;
  let fixture: ComponentFixture<FirstBlockComponent>;
  let spy: jasmine.Spy;
  let getjson: GetJsonService;

  const getJsonStub = {
    getPagesJson() {
      const data = [{h1: 'H1 with "Value"'}];
      return of( data );
    }
  };

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ FirstBlockComponent ],
      providers: [ 
        { provide: GetJsonService, useValue: getJsonStub } 
      ]
    })
    .compileComponents();
    getjson = TestBed.get(GetJsonService);
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(FirstBlockComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
// Errors:
// Uncaught TypeError: Cannot read property '0' of undefined thrown


  it('should called getPagesJson', async(() => {
    spy = spyOn(getjson, 'getPagesJson');
    fixture.detectChanges();
    fixture.whenStable().then(() => {
      expect(spy).toBeTruthy(); 
    });
  }));
// Errors:
// Failed: Cannot read property '0' of undefined


// I'm using very simple and non asynchronous approach below
  it('should get H1 by getJsonService.getPagesJson()', () => {
    getjson.getPagesJson(); 
    fixture.detectChanges();
    expect(component.h1).toBe('H1 with "Value"');
  });
// Errors:
// Uncaught TypeError: Cannot read property '0' of undefined thrown
// Expected undefined to be 'H1 with "Value"'.


});

The tests are telling you that something is wrong, which is good. The errors in your comments tell you exactly what the issue is.

Cannot read property '0' of undefined is saying it cannot find 0 of undefined. Therefore this part is failing: data["index"][0] , and if 0 cannot be found on undefined then data["index"] must be undefined .

Inside first-block.component.ts the subscription sets this.h1=data["index"][0]["h1"] which then you have mocked in your test as const data = [{h1: 'H1 with "Value"'}]; . These do not match. Instead you should have inside your test const data = {index: [{h1: 'H1 with "Value"'}]}; - this could be a good opportunity to create an interface for the data model since you have run into it as an issue.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM