1
votes

I am trying to learn how to set up a Unit test using Jasmine and Angular 2 with Async. I have a component which calls a service. But I seem to hit a road block. I have a service like below: item.service.ts

import { Injectable } from "@angular/core";
import { Http, Response } from "@angular/http";
import { Observable } from "rxjs/Observable";
import { Item } from "./item";
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';

@Injectable()
export class ItemService {
    private baseUrl = 'api/items/';  // web api URL 
    constructor(private http: Http) { }   

    // calls the [GET] /api/items/GetLatest/{n} Web API method to retrieve the latest items. 
    getLatest(num?: number) {
        let url = this.baseUrl + 'GetLatest/';
        if (num != null) { url += num; }
        return this.http.get(url)
            .map(response => response.json())
            .catch(this.handleError);
    }
}

I am calling the service in a component (item-list.component.ts) as below:

import { Component, OnInit } from '@angular/core';
import { Item } from './item';
import { ItemService } from './item.service';
import { GridModule } from '@progress/kendo-angular-grid';
import { GroupDescriptor, process } from '@progress/kendo-data-query';
import { SortDescriptor, orderBy } from '@progress/kendo-data-query';
import { GridDataResult } from '@progress/kendo-angular-grid';
import '@telerik/kendo-theme-default/dist/all.css';
@Component({
    selector: 'itemlist',       
    templateUrl: 'someItems.component.html',       

})

export class ItemListComponent implements OnInit {
    selectedItem: Item;
    items: Item[];
    errorMessage: string;
     groups: GroupDescriptor[];
     sort: SortDescriptor[] = [];
     gridView: GridDataResult;
    constructor(private itemService: ItemService) { }

    ngOnInit() {
        this.getLatest();
    }

    getLatest() {
        this.itemService.getLatest(5)
            .subscribe(latestItems => {
                console.log(latestItems);
                this.items = latestItems;
                this.gridView = {
                    data: orderBy(this.items, this.sort),
                    total: this.items.length
                };
            }, error => {
                console.log('error');
                this.errorMessage = <any>error;
            });
    }   

} 

This is my item.component.spec.ts file

import { async, inject, ComponentFixture, TestBed } from '@angular/core/testing';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ItemListComponent } from '../App/item-list.component';
import { ItemService } from '../App/item.service';
import { GridModule } from '@progress/kendo-angular-grid';
import { HttpModule, Http } from "@angular/http";
import { Observable } from 'rxjs/Rx';
import 'rxjs/add/observable/of';

class MockMyService {       
    public items: Array<any>;
    public getLatest(num?: number) {
        this.items = [{ Id: 1, Title: "test1", Description: "test12" }]
        return Observable.of(this.items);

    }
}

describe('Component:ItemList', () => {
    let fixture: ComponentFixture<ItemListComponent>;
    let itemService: MockMyService;

    describe('Async', () => {
        beforeEach(async(() => { 
            TestBed.configureTestingModule({
                declarations: [
                    ItemListComponent
                ],
                providers: [
                    //ItemService
                    { provide: ItemService, useClass: MockMyService }
                ],
                imports: [
                    GridModule,HttpModule
                ],
                schemas: [CUSTOM_ELEMENTS_SCHEMA]
            });

            fixture = TestBed.createComponent(ItemListComponent);
            itemService = fixture.debugElement.injector.get(ItemService);   

             spyOn(itemService, 'getLatest')
               .and.returnValue({ subscribe: () => { itemService.items} });                     

            fixture.detectChanges();               

        }));

        afterEach(() => {
            fixture = undefined;//teardown
        });                             

        it('should get items', () => {
             itemService.getLatest();
             expect(fixture.debugElement.componentInstance.items.length).toEqual(1);
        });    

    });
});

But the test throws error as below - Chrome 57.0.2987 (Windows 7 0.0.0) Component:ItemList Async should get items FAILED TypeError: Cannot read property 'length' of undefined I will really appreciate if anyone can help me out..

1

1 Answers

1
votes

Dude look at your logic

class MockMyService {       
  public items: Array<any>;
  public getLatest(num?: number) {
    this.items = [{ Id: 1, Title: "test1", Description: "test12" }]
    return Observable.of(this.items);
  }
}

spyOn(itemService, 'getLatest')
  .and.returnValue({ subscribe: () => { itemService.items} }); 

In your spy (which overrides the real method), you're returning the itemService.items, which isn't even initialized until you call the getLatest. That's why it's undefined.

If you are already mocking the data, just forget the spy. If you take it out, it should work. If you want to change the data for each test, then pass an actual value to it, instead of using the uninitialized itemService.items