Javascript 无法理解错误:TypeError:无法读取属性';订阅';未定义的

Javascript 无法理解错误:TypeError:无法读取属性';订阅';未定义的,javascript,angular,typescript,rxjs,integration-testing,Javascript,Angular,Typescript,Rxjs,Integration Testing,我编写了一个从API调用获取数据的实现。但是,在测试功能时,即使在编写任何有意义的测试用例之前,我也会遇到以下错误: TypeError: Cannot read property 'subscribe' of undefined at DataComponent.ngOnInit (http://localhost:9876/_karma_webpack_/webpack:/src/app/dashboard/job/job.component.ts:48:10) at cal

我编写了一个从API调用获取数据的实现。但是,在测试功能时,即使在编写任何有意义的测试用例之前,我也会遇到以下错误:

TypeError: Cannot read property 'subscribe' of undefined
    at DataComponent.ngOnInit (http://localhost:9876/_karma_webpack_/webpack:/src/app/dashboard/job/job.component.ts:48:10)
    at callHook (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:3405:1)
    at callHooks (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:3375:1)
    at executeInitAndCheckHooks (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:3327:1)
    at refreshView (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:8573:1)
    at renderComponentOrTemplate (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:8672:1)
    at tickRootContext (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:9885:1)
    at detectChangesInRootView (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:9910:1)
    at RootViewRef.detectChanges (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:10320:1)
    at ComponentFixture._tick (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/testing.js:243:1)
我不确定我错过了什么,任何帮助都将不胜感激。 我研究了以下内容以获得一些理解:

  • ->解决方案在我的情况下不起作用
和其他一些参考资料

这是我的代码文件。我已经删除了代码中我认为与当前问题无关的某些部分

dataService.ts代码:

import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { IData } from '../model/data.model';
import { Observable, throwError } from 'rxjs';
import {catchError, tap} from 'rxjs/operators';

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

  constructor(private http: HttpClient) { }

  public getData(): Observable<IData[]>{
    return this.http.get<IData[]>('url')
    .pipe(
      tap(data => console.log('Data Received')),
      catchError(this.handleError)
    );
  }
  private handleError(err: HttpErrorResponse){
   //handle error code
  }
}

测试文件:data.spec.ts:

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DataComponent } from './job.component';
import { IData } from 'src/app/model/job.model';
import { DataService } from 'src/app/service/job-data.service';
import { of, Observable } from 'rxjs';
import { NO_ERRORS_SCHEMA } from '@angular/core';

describe('DataComponent', () => {
  let component: DataComponent;
  let fixture: ComponentFixture<DataComponent>;
  let mockDataService ;
  let JOBS: IData[];
  

  beforeEach(async () => {
    mockJobDataService = jasmine.createSpyObj(['getData']);
    await TestBed.configureTestingModule({
      declarations: [ DataComponent ],
      providers: [
        {provide: DataService, useValue: mockDataService}
      ]
    })
    .compileComponents()
    .then(() => {
      DATA = [{id: 'CJH'}];
      fixture = TestBed.createComponent(DataComponent);
      component = fixture.componentInstance;
      fixture.detectChanges();  //updates bindings
    });
  });

  it('should return true',() => {
        expect(true).toBe(true);
  })
  
});

从'@angular/core/testing'导入{ComponentFixture,TestBed};
从“./job.component”导入{DataComponent};
从'src/app/model/job.model'导入{IData};
从'src/app/service/job data.service'导入{DataService};
从“rxjs”导入{of,Observable};
从'@angular/core'导入{NO_ERRORS_SCHEMA};
描述('DataComponent',()=>{
let组件:数据组件;
let夹具:组件夹具;
让mockDataService;
let JOBS:IData[];
beforeach(异步()=>{
mockJobDataService=jasmine.createSpyObj(['getData']);
等待TestBed.configureTestingModule({
声明:[DataComponent],
供应商:[
{提供:DataService,useValue:mockDataService}
]
})
.compileComponents()
.然后(()=>{
数据=[{id:'CJH'}];
fixture=TestBed.createComponent(DataComponent);
组件=fixture.componentInstance;
fixture.detectChanges();//更新绑定
});
});
它('应该返回true',()=>{
期望(真),期望(真);
})
});

当我删除fixture.detectChanges()时,错误被删除。但据我所知,即使我在测试中的任何地方使用此调用,测试用例也应该可以工作。

您正在使用
mockJobDataService=jasmine.createSpyObj(['getData'])创建一个spy对象并正在使用此
{provide:DataService,useValue:mockDataService}
正确注册它。在这一点上,您的组件应该正确地创建,并将您的伪服务注入其中。问题在于,您没有在伪服务上设置预期的方法调用

只要您在调用
fixture.detectChanges()
之前这样做,下面的内容就可以解决这个问题。由于您没有包括
IJob
界面的形状,我不能确切地告诉您,但我通过将其转换为(
({}作为IJob)
)使其与typescript一起工作

从'rxjs'导入{of}

mockJobDataService.getData.and.returnValue(of([({}as IJob)])

这告诉jasmine任何使用这个伪数据服务的东西,在调用getData方法时,该方法的返回应该是
of([({}as IJob)]
(这是IJob数组类型的可观察值)


当您继续编写实际正在测试组件的测试时,您可能希望将模拟方法和“detectChanges”调用移动到每个测试中,以便为每个测试提供不同的假数据。

对于错误表示歉意。为了发布这个问题,我更改了组件和变量的名称,但遗漏了几个地方。将所有发生的“作业”替换为“数据”。(如JobComponent->DataComponent、JOBS->DATA等)为什么不为“mockDataService”模拟值定义一个“getData”假实现?@habibchabbi你是说这个吗?mockDataService={getData:()=>{return of(DATA);}}}是的,这不起作用吗?是的,它现在起作用了。我早些时候犯了一个愚蠢的错误。谢谢,我试过了。为我工作。谢谢
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DataComponent } from './job.component';
import { IData } from 'src/app/model/job.model';
import { DataService } from 'src/app/service/job-data.service';
import { of, Observable } from 'rxjs';
import { NO_ERRORS_SCHEMA } from '@angular/core';

describe('DataComponent', () => {
  let component: DataComponent;
  let fixture: ComponentFixture<DataComponent>;
  let mockDataService ;
  let JOBS: IData[];
  

  beforeEach(async () => {
    mockJobDataService = jasmine.createSpyObj(['getData']);
    await TestBed.configureTestingModule({
      declarations: [ DataComponent ],
      providers: [
        {provide: DataService, useValue: mockDataService}
      ]
    })
    .compileComponents()
    .then(() => {
      DATA = [{id: 'CJH'}];
      fixture = TestBed.createComponent(DataComponent);
      component = fixture.componentInstance;
      fixture.detectChanges();  //updates bindings
    });
  });

  it('should return true',() => {
        expect(true).toBe(true);
  })
  
});