I have a simple unit test that is done using karma/jasmine on an Angular 6 component. From the pluralsight course and documentation I have gathered it appears I am mocking my service correctly that my component requires, but when the method is called to return data from the mock service I get the error saying property subscribe is undefined.
My "it" function is empty because the test fails once the component is constructed in the beforeEach method. The constructor of the component is what calls the method I am attempting to test. Please see my code below.
MainComponent
import { Component, Input, Output, OnInit, EventEmitter } from '@angular/core';
import { DataService } from '../services/data.service';
import { TableComponent } from './table/table.component';
@Component({
selector: 'main',
templateUrl: './main.component.html',
styleUrls: ['./main.component.css']
})
export class MainComponent {
columns: string[] = ['PrismEmployeeID', 'LastName', 'FirstName', 'Initials', 'LastFour', 'BirthDate', 'HireDate', 'OfficePhone', 'OfficeFax', 'ClassificationDescription', 'SupervisorName', 'DepartmentDescription', 'CountryName'];
loading: boolean = false;
@Input() tableData: Employee[];
constructor(private _dataService: DataService) {
this.loadEmployees();
}
loadEmployees() {
this.loading = true;
this._dataService.GetEmployees().subscribe((data) => {
this.loading = false;
this.tableData = data.json() as Employee[];
});
}
onLoading(loading: boolean) {
this.loading = loading;
}
onReloadData(reloadData: boolean) {
this.loadEmployees();
}
}
interface Employee {
PrismEmployeeID: number;
FirstName: string;
LastName: string;
Initials: string;
NickName: string;
SSN: string;
DateOfBirth: string;
HireDate: string;
OfficePhone: string;
OfficeFax: string;
ClassificationID: number;
ClassificationDescription: string;
SupervisorID: number;
DepartmentID: number;
DepartmentDescription: string;
SupervisorName: string;
CountryID: number;
CountryName: string;
_selected: boolean;
}
<br/>
<h2>Prism Employees</h2>
<div *ngIf="loading" class="d-flex justify-content-center bd-highlight mb-3">
<div class="p-2 bd-highlight"></div>
<div class="p-2 bd-highlight">
<mat-spinner id="overlay"></mat-spinner>
</div>
<div class="p-2 bd-highlight"></div>
</div>
<div *ngIf='tableData'>
<table-component #table (loading)="onLoading($event)" (reloadData)="onReloadData($event)" [data]="tableData" [displayedColumns]="columns"></table-component>
</div>
The issue is that you are currently providing a stub of your service with no configured return for the GetEmployees
method. Meaning that once the component invokes the previous function and subscribe to its (undefined) return, it will trigger an exception.
To solve this, you need to fake the return of that method. Based on this answer you can try this as follows:
import {of} from 'rxjs';
...
mockDataService = jasmine.createSpyObj(DataService.Name, {'GetEmployees': of(EMPLOYEES)});
...
UPDATE:
In order for this to work though, you will have to refactor your DataService.GetEmployees
method to have the following signature:
GetEmployees(): Observable<Employee[]>;
The current implementation of DataService.GetEmployees
is a leaky abstraction, as it returns the raw Response
object from the old Http
API, forcing the consumer (in this case the component) to know details about the underlying implementation (this detail being the use of data.json() as Employee[]
)