Angular component test error: TypeError Cannot read property 'subscribe' of undefined

Angular component test error: TypeError Cannot read property 'subscribe' of undefined

我有一个简单的单元测试,它是在 Angular 6 组件上使用 karma/jasmine 完成的。从我收集的 pluralsight 课程和文档来看,我似乎正在正确地模拟我的服务,我的组件需要,但是当该方法被调用到来自模拟服务的 return 数据时,我收到错误消息 属性 subscribe未定义。

我的 "it" 函数是空的,因为在 beforeEach 方法中构造组件后测试失败。组件的构造函数调用了我试图测试的方法。请在下面查看我的代码。

import { TestBed, async, ComponentFixture } from '@angular/core/testing';
import { MainComponent } from './main.component';
import { DataService } from '../services/data.service';
import { of } from 'rxjs';
import { NO_ERRORS_SCHEMA } from "@angular/core";

describe('Should construct MainComponent', () => {
 let mainComponent: MainComponent;
 let EMPLOYEES;
 let fixture: ComponentFixture<MainComponent>;
 let mockDataService;

 beforeEach(() => {
  EMPLOYEES = [
   {
    "PrismEmployeeID": 1,
    "FirstName": "install2",
    "LastName": "account",
    "Initials": "IA ",
    "NickName": "",
    "SSN": "",
    "DateOfBirth": "09/26/2014",
    "HireDate": "09/26/2014",
    "OfficePhone": "9043943239",
    "OfficeFax": "9042246400",
    "ClassificationID": 133,
    "ClassificationDescription": "Claims Support U.S. Insurance",
    "SupervisorID": 32,
    "DepartmentID": 2,
    "DepartmentDescription": "Information Technology",
    "SupervisorName": "Jerry Sutton",
    "CountryID": 1,
    "CountryName": "United States"
   }
  ];

  mockDataService = jasmine.createSpyObj(['GetEmployees']);

  TestBed.configureTestingModule({
   declarations: [MainComponent],
   providers: [{ provide: DataService, useValue: mockDataService }],
   schemas: [NO_ERRORS_SCHEMA]
  });

  fixture = TestBed.createComponent(MainComponent);
  fixture.detectChanges();

  mockDataService = jasmine.createSpyObj(['GetEmployees']);
 });

 it('should get an array Employees',
  () => {});
});

主要组件

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>

问题是您当前提供的服务存根没有为 GetEmployees 方法配置 return。意思是一旦组件调用上一个函数并订阅它的(未定义)return,它就会触发异常。

为了解决这个问题,你需要伪造那个方法的return。基于 this answer 你可以尝试如下:

import {of} from 'rxjs';
...
mockDataService = jasmine.createSpyObj(DataService.Name, {'GetEmployees': of(EMPLOYEES)});
...

更新:

为了使其正常工作,您必须重构 DataService.GetEmployees 方法以具有以下签名:

GetEmployees(): Observable<Employee[]>;

DataService.GetEmployees 的当前实现是 leaky abstraction,因为它 return 是来自旧 Http [=34= 的原始 Response 对象],迫使消费者(在本例中为组件)了解有关底层实现的详细信息(此详细信息是 data.json() as Employee[] 的使用)

spyOn(mockYourService, 'getSomeData').and.returnValue({ subscribe: () => {} })

你可以... Return 间谍中的实际可观察​​者

spyOn(mockYourService, 'getSomeData').and.returnValue(Observable.of(someData)) 也许这比循环订阅方法更受欢迎,因为如果对服务的调用正在改变组件中的某些东西,这就是您可能想要测试的东西