Karma error: "Failed: Cannot read property 'subscribe' of undefined"

Karma error: "Failed: Cannot read property 'subscribe' of undefined"

我正在使用 Angular 8 和 Karma。 Karma 的新手,所以我希望这很简单。

当我 运行 我的单元测试时,我得到 "Failed: Cannot read property 'subscribe' of undefined"。调用堆栈指示我正在使用订阅的 TimesheetComponent.getUserRole 方法。因此,错误是该组件方法中的 datasvc 在 Karma 中未定义。不知道怎么定义。

我已经在组件使用的服务上创建了 createSpyObj,但我仍然收到错误。这是代码-

tk-timesheet.component.ts-

import { Component, OnInit } from '@angular/core';
import 'ag-grid-community';
import { throwError, Subscription, forkJoin } from 'rxjs';
 import {GridOptions} from 'ag-grid-community';

import { TkDataService } from '../tk-services/tk-data/tk-data.service';

interface DayAssignments {
  dayOfWeek: string;
  date: string;
}

@Component({
 selector: 'app-timesheet',
 templateUrl: './tk-timesheet.component.html',
 styleUrls: ['./tk-timesheet.styles.scss']
})

export class TimesheetComponent implements OnInit {
  public gridOptions: GridOptions;
  public columnDefs: any;
  public defaultColDef: any;
  public rowData: any;
  public chargeCodes: any;
  public weekOfOptions: any;
  public weekDates: object[] = [];
  public userRoles: {};
  public shifts: []
  public thisUser: {User}
  public thesePayPeriods: any
  public thisPayPeriod: any
  public commentCode: any
  public commentCodes: any
  subscription: Subscription;
  weekOf: any = 'current';
  comment = '';


  constructor(
   private dataSvc: TkDataService
  ) {
  this.gridOptions = {
      enableSorting: true,
      enableFilter: false,
      rowStyle: {color: '#84613D'},
      headerStyle: {color: '#84613D'}

    } as GridOptions;

  this.defaultColDef = [
        {
        resizable: true,
      }
    ];


  this.weekOfOptions = [
        'current',
        'previous'
    ];
  this.commentCodes = [
    'CH', 'PTO', 'HOL'

  ]
  }


   private canWrite: boolean;

   getUserRole(): any {
     return this.dataSvc.post('getuserdata', {})
     .subscribe(res =>  {
       this.userRoles = res;
       console.log('this.userRoles inside tk-timesheet component::: ', this.userRoles);
    });
   }


    forkJoin([
      this.dataSvc.getUserId('userid', ''),
      this.dataSvc.getOpenPayPeriods("payperiods/open", {status: "open"}),
      this.dataSvc.getChargeCodes('chargeCodes', '')
    ])
    .subscribe((data) => { 
      let userData = data[0]
      let payPeriods = data[1]
      this.thesePayPeriods = payPeriods
      this.weekOfOptions = this.thesePayPeriods.map(period => {
        let dateString = period.startdate  //use thesePayPeriods to set weekOfOptions options.
        let newDate = new Date(dateString.replace('T', ' '))
        let shortNewDate = newDate.toString().split(' ')
        console.log('shortNewDate', shortNewDate)
        let result = `${shortNewDate[0]} ${shortNewDate[1]} ${shortNewDate[2]} ${shortNewDate[3]}`
        return result

      })
      this.thisPayPeriod = payPeriods[0]  //use weekOfOptions selection to set this.thisPayPeriod dynamically.
      let chargeCodes = data[2]
      this.chargeCodes = chargeCodes
      console.log('data  from forkJoin:: ', data, 'userData::: ', userData[0].userid, 'payPeriods:: ', payPeriods, 'chargeCodes:: ', chargeCodes)
      this.dataSvc.getShifts(`shifts/${userData[0].userid}/${payPeriods[0].payperiodid}`, data).subscribe((shifts: any) => {
        this.shifts = shifts
        this.rowData = this.buildRowData(shifts, chargeCodes)
      })

    }, (err) => { /* do error stuff*/})


    //this.getUserRole();
    this.getUserRole();
    this.getMonday();


 }

tk-timesheet.spec.ts-

 import { TestBed, async, ComponentFixture, inject, tick, fakeAsync} from '@angular/core/testing';
 import { TimesheetComponent } from './tk-timesheet.component';
 import { HttpClientTestingModule } from '@angular/common/http/testing';
 import { of } from 'rxjs';
 import { TkDataService } from '../tk-services/tk-data/tk-data.service';

describe('TimesheetComponent', async () => {


let dataService;
  // tslint:disable-next-line: max-line-length
dataService = jasmine.createSpyObj('TkDataService', ['get', 'post', 'getUserId', 'getOpenPayPeriods', 'getChargeCodes', 'getUserRole', 'getuserdata']);

let component: TimesheetComponent;
let fixture: ComponentFixture<TimesheetComponent>;

beforeEach( async( () => {
 TestBed.configureTestingModule({
    declarations: [ TimesheetComponent ],
    imports: [ HttpClientTestingModule ],
    providers: [{ provide: TkDataService,  useValue: dataService }]
  })
  .compileComponents()

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

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

it('should populate column headers with monday of the current week on initial view ', async(() => {
    fixture.detectChanges();

    let thisMonday = () => {
        let date = new Date();
        let day = date.getDay();
        let diff = date.getDate() - day + (day == 0 ? -6 : 1); // adjust when day is sunday
        let mon = new Date(date.setDate(diff));

        return `${mon.getMonth() + 1}/${mon.getDate()}/${mon.getFullYear().toString().slice(-2)}`;
    };
    spyOn(component, 'getUserRole').and.returnValue(of({}));;
    expect(component.weekDates[0]['date']).toEqual(null);
}));

TkDataservice-

 import { environment } from 'src/environments/environment';
 import { Injectable } from '@angular/core';
 import { HttpClientModule, HttpClient, HttpHeaders, HttpResponse, HttpParams } from '@angular/common/http';
 import { Observable } from 'rxjs';
 import { OidcSecurityService } from 'angular-auth-oidc-client';
 import { TimekardResponse } from 'src/app/tk-models/tk-response';
 import { AuthService } from '../auth.service';

 @Injectable({
    providedIn: 'root'
 })
 export class TkDataService {
  constructor(
    private http: HttpClient
    ) {

    }


post(route: string, data: any, responseType?){
    if(localStorage.getItem('userRole') === null || localStorage.getItem('userRole') === undefined) {
        this.getUserRole();
    }

    const observe = responseType === 'blob' ? 'response' : 'body';

    let requestHeaders = new HttpHeaders();
    requestHeaders = requestHeaders.set('Authorization', localStorage.getItem('bearerToken'));
     // tslint:disable-next-line: max-line-length
    return this.http.post<TimekardResponse>(environment.baseAPIUrl + route, data, {withCredentials: false, headers: requestHeaders, responseType,      observe: observe as 'body'} );
}


}}

在此先感谢您的帮助!

您的代码组件不完整。我假设您调用 getUserRole() 的位置在 ngOnInit 方法内部。所以,如果是,在你的测试中你必须首先定义 spyOn,在 fixture.detectChanges

之前
it('should populate column headers with monday of the current week on initial view ', async(() => {

    spyOn(component, 'getUserRole').and.returnValue(of({}));
    fixture.detectChanges();

    let thisMonday = () => {
        let date = new Date();
        let day = date.getDay();
        let diff = date.getDate() - day + (day == 0 ? -6 : 1); // adjust when day is sunday
        let mon = new Date(date.setDate(diff));

        return `${mon.getMonth() + 1}/${mon.getDate()}/${mon.getFullYear().toString().slice(-2)}`;
    };

    expect(component.weekDates[0]['date']).toEqual(null);
}));