【问题标题】:Cannot read property 'getAboutInfo' of undefined at <Jasmine>无法读取 <Jasmine> 处未定义的属性“getAboutInfo”
【发布时间】:2020-07-02 16:12:01
【问题描述】:

这是我的 Angular 代码,功能运行良好,但测试用例失败。请告诉我我在代码中做错了什么? 我得到的错误

HeadlessChrome 83.0.4103 (Windows 10.0.0) AboutComponent 应该创建 FAILED TypeError:无法读取未定义的属性“getAboutInfo” 在 ** 在 AboutComponent.ngOnInit (http://localhost:9876/karma_webpack/src/app/about/about.component.ts:44:28) 在 callHook (http://localhost:9876/karma_webpack/node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:3937:1) 在 callHooks (http://localhost:9876/karma_webpack/node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:3901:1) 在 executeInitAndCheckHooks (http://localhost:9876/karma_webpack/node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:3842:1) 在 refreshView (http://localhost:9876/karma_webpack/node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:11795:1) 在 renderComponentOrTemplate (http://localhost:9876/karma_webpack/node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:11903:1) 在 tickRootContext (http://localhost:9876/karma_webpack/node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:13379:1) 在 detectChangesInRootView (http://localhost:9876/karma_webpack/node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:13413:1) 在 RootViewRef.detectChanges (http://localhost:9876/karma_webpack/node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:15093:22) 在 ComponentFixture._tick (http://localhost:9876/karma_webpack/node_modules/@angular/core/ivy_ngcc/fesm2015/testing.js:323:1)

import { async, ComponentFixture, TestBed} from '@angular/core/testing';
import { AboutComponent } from './about.component';
import { AboutService } from './about.service';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { Observable, of } from 'rxjs';
import { I18nService } from 'src/utils/i18n.service';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { AppModule } from './../app.module';

describe('AboutComponent', () => {
  let component: AboutComponent;
  let fixture: ComponentFixture<AboutComponent>;
  let dialogSpy: jasmine.Spy;
  let app: any;
  const mockDialogRef = {
    close: jasmine.createSpy('close')
  };
  let service: any;
  const data = '20/04/2019';
  let getAboutInfoSpy: any;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [AboutComponent],
      imports: [HttpClientTestingModule , AppModule],
      providers: [{ provide: AboutService, useValue: service },
        I18nService,
            { provide: MAT_DIALOG_DATA, useValue: {} },
            { provide: MatDialogRef, useValue: mockDialogRef}]
    }).compileComponents();
  }));

  beforeEach(async () => {
    fixture = TestBed.createComponent(AboutComponent);
    component = fixture.componentInstance;
    await fixture.whenStable();
    fixture.detectChanges();
  });

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

  it('infoList should be empty array', () => {
    expect(app['dataList'].length).toBe(0);
  });

  it('when OnInit invoked through service data will return to infoList ', async(() => {
    service = fixture.debugElement.injector.get(AboutService);
    spyOn(service, 'getAboutInfo').and.returnValue(of(data));
    app.ngOnInit();
    expect(app['dataList'].length).toBe(3);
  }));

  it('onCancel should close the dialog', async( () => {
    component.closePopup();
    expect(mockDialogRef.close).toHaveBeenCalled();
  }));

});
import { Component, OnInit, Inject } from '@angular/core';
import { AboutService } from './about.service';
import { Subscription } from 'rxjs';
import { MatDialogRef} from '@angular/material/dialog';
import { I18nService } from 'src/utils/i18n.service';

@Component({
  selector: 'app-about',
  templateUrl: './about.component.html',
  styleUrls: ['./about.component.scss']
})
export class AboutComponent implements OnInit {

  private aboutServiceSubscription: Subscription;
  dataList: any;
  locales: any = {};
  translator: any;
  
  constructor(
    private dialogRef: MatDialogRef<AboutComponent>,
    public aboutService: AboutService,
    private i18Service: I18nService) {}


  ngOnInit() {
    this.translator = this.i18Service.getTranslator();
    this.translator.translateObject.subscribe((item: any) => {
      this.locales = item;
    });
    this.aboutServiceSubscription =  this.aboutService.getAboutInfo().subscribe((data: any) => {
      if (data) {
        data = data.split('/');
        this.dataList = data;
      }
    });
  }


   /**
    * Closes the poup
    * @memberof AboutComponent
    */
  closePopup() {
    this.dialogRef.close();
  }
}
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';

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

  constructor(private http: HttpClient) {

   }

   getAboutInfo() {
    return this.http.get('/assets/aboutInfo.txt', {responseType: 'text'})
  }
}

【问题讨论】:

  • providers: [{ provide: AboutService, useValue: service }, service 变量的值
  • 我该怎么做?
  • service = &lt;something&gt;。如service = jasmine.createObjectSpy('AboutService', ['getAboutInfo'])。但是,由于服务是providedIn: 'root',您根本不必这样做。您应该能够删除该提供程序行,然后使用TestBed.inject(AboutService) 来获取注入到您的组件中的服务。

标签: javascript angular typescript unit-testing karma-jasmine


【解决方案1】:

错误信息表明你没有正确模拟AboutService,你将undefined传递给useValue,所以通过Injector获取的AboutServiceundefined。您不能将spyOn 用于undefined 值。

这是一个工作示例,删除了不相关的代码:

about.component.ts:

import { Component, OnInit } from '@angular/core';
import { AboutService } from './about.service';
import { Subscription } from 'rxjs';

@Component({
  selector: 'app-about',
})
export class AboutComponent implements OnInit {
  private aboutServiceSubscription: Subscription;
  dataList: any;
  locales: any = {};
  translator: any;

  constructor(public aboutService: AboutService) {}

  ngOnInit() {
    this.aboutServiceSubscription = this.aboutService
      .getAboutInfo()
      .subscribe((data: any) => {
        if (data) {
          data = data.split('/');
          this.dataList = data;
        }
      });
  }
}

about.service.ts:

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';

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

  getAboutInfo() {
    return this.http.get('/assets/aboutInfo.txt', { responseType: 'text' });
  }
}

about.component.spec.ts:

import { HttpClientTestingModule } from '@angular/common/http/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { of } from 'rxjs';
import { AboutComponent } from './about.component';
import { AboutService } from './about.service';

fdescribe('62700708', () => {
  let component: AboutComponent;
  let fixture: ComponentFixture<AboutComponent>;
  let aboutServiceSpy: jasmine.SpyObj<AboutService>;
  const data = '20/04/2019';

  beforeEach(() => {
    aboutServiceSpy = jasmine.createSpyObj('AboutService', ['getAboutInfo']);
    aboutServiceSpy.getAboutInfo.and.returnValue(of(data));

    TestBed.configureTestingModule({
      declarations: [AboutComponent],
      imports: [HttpClientTestingModule],
      providers: [{ provide: AboutService, useValue: aboutServiceSpy }],
    }).compileComponents();
  });

  beforeEach(async () => {
    fixture = TestBed.createComponent(AboutComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

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

  it('when OnInit invoked through service data will return to infoList ', () => {
    expect(aboutServiceSpy.getAboutInfo).toHaveBeenCalledTimes(1);
    expect(component.dataList).toEqual(['20', '04', '2019']);
  });
});

单元测试结果:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-04-23
    • 2020-01-18
    • 2021-02-27
    • 1970-01-01
    • 1970-01-01
    • 2018-02-02
    • 1970-01-01
    • 2015-07-01
    相关资源
    最近更新 更多