【问题标题】:Angular Unit Test if Component calls Http Service method : Error: <spyOn> : could not find an object to spy upon如果组件调用 Http 服务方法,则 Angular 单元测试:错误:<spyOn>:找不到要监视的对象
【发布时间】:2020-07-22 19:54:15
【问题描述】:

我的目的是测试组件方法是否调用服务方法。服务方法实现是一个 Post,但是我对 post 机制不感兴趣,因为我计划在服务中测试它,而我只想知道是否调用了正确的服务方法。

我是单元测试的新手,但是错误表明 spyon 的用法如下用法 spyOn('object, methodName'),表明我没有正确创建要测试的服务对象的实例,但是为了创建它需要构造函数中的 HttpClient 的服务对象,我已经尝试了很多解决方案并且我已经阅读了几个小时,非常感谢帮助。

最好, SC

单元测试

describe('SchedulerComponent', () => {
  let component : SchedulerComponent;
  let fixture : ComponentFixture<SchedulerComponent>;
  let schedulerReportService : SchedulerReportService;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ SchedulerComponent ],
      imports: [BrowserAnimationsModule,
                MatSlideToggleModule,
                MatRadioModule,
                MatFormFieldModule,
                MatInputModule,
                MatSelectModule,
                MatNativeDateModule,
                MatDatepickerModule,
                NativeDateModule,
                NgxMaterialTimepickerModule,
                ReactiveFormsModule,
                FormsModule,
                HttpClientTestingModule],
      schemas:[CUSTOM_ELEMENTS_SCHEMA],
      providers: [{provide : SchedulerReportService}]
    })
    .compileComponents();
  }));

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

    fixture.detectChanges();
  });

  fit('onSubmit should call scheduleReportService',inject([SchedulerReportService], (service: SchedulerReportService) => {

    let scheduleServiceSpy = spyOn(schedulerReportService, 'submitScheduledReport') ;
    let submitReportSpy = spyOn(component, 'onSubmit'); 

    submitReportSpy.and.callThrough();

    expect(scheduleServiceSpy).toHaveBeenCalled();


  }))

对应的服务代码如下

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

  constructor(private http: HttpClient) { }

   submitScheduledReport(servicerequest: ScheduleService) {

    console.log(servicerequest)

    // const headers = new HttpHeaders({'key':'value'});
    // return this.http.post<ScheduleService>(localUrl, servicerequest, {headers : headers} ).pipe(
    //   retry(1), catchError(this.handleError<ScheduleService>('scheduled report post error')));

  }

组件包含触发服务的方法

export class SchedulerComponent implements OnInit {

  constructor(private fb: FormBuilder,
              private schedulerReportService: SchedulerReportService) { 
    onSubmit(){

    let report = this.schedulerForm.value;
    let scheduleServiceModel = new ScheduleService(report)
    this.schedulerReportService.submitScheduledReport(scheduleServiceModel);

  }
}

【问题讨论】:

    标签: angular unit-testing jasmine karma-jasmine


    【解决方案1】:

    要专门解决上述代码中的问题,您需要在调用spyOn 之前设置服务实例。像这样的:

     schedulerReportService = fixture.debugElement.injector.get(SchedulerReportService);
    
      ...
    
     let scheduleServiceSpy = spyOn(schedulerReportService, submitScheduledReport');
    

    不过,我推荐另一种方法,即使用服务间谍存根。由于您不需要测试您的服务,只需调用一个服务方法,您就不需要注入原始的SchedulerReportService,而是可以像这样向 Angular 提供服务对象间谍:

    describe('SchedulerComponent', () => {
      let component : SchedulerComponent;
      let fixture : ComponentFixture<SchedulerComponent>;
      let schedulerReportService = jasmine.createSpyObj('SchedulerReportService', ['submitScheduledReport']);
    
      beforeEach(async(() => {
        TestBed.configureTestingModule({
          declarations: [ SchedulerComponent ],
          imports: [BrowserAnimationsModule,
                    MatSlideToggleModule,
                    MatRadioModule,
                    MatFormFieldModule,
                    MatInputModule,
                    MatSelectModule,
                    MatNativeDateModule,
                    MatDatepickerModule,
                    NativeDateModule,
                    NgxMaterialTimepickerModule,
                    ReactiveFormsModule,
                    FormsModule,
                    HttpClientTestingModule],
          schemas:[CUSTOM_ELEMENTS_SCHEMA],
          providers: [{provide : SchedulerReportService, useValue: schedulerReportService}]
        })
        .compileComponents();
    
        fixture = TestBed.createComponent(SchedulerComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
      }));
    
      it('onSubmit should call scheduleReportService', () => {
        component.onSubmit();
        expect(schedulerReportService.submitScheduledReport).toHaveBeenCalled();
      }));

    通过这种方式,您可以测试 schedulerReportService.submitScheduledReport 是否被调用,而没有原始服务 http 调用的开销。

    【讨论】:

    • 在应用你的方法时,我应该像我一样从单元测试功能中删除注入吗?
    • @sir_camel 是的,因为您是按价值提供服务,所以您不需要它。
    • 这很有道理,谢谢。我已经应用了您解决方案的最后 3 个代码语句,但是我不确定应该在哪里应用您答案的第一个代码块中的两个语句。我是否需要在 BeforeEach 或测试本身中应用fixture.DebugElement.inject():当前测试返回:预期的间谍 SchedulerReportService.submitScheduledReport 已被调用。
    • @sir_camel 抱歉,我不太明白您的最后评论。我用更多细节更新了我的答案。希望对您有所帮助。
    • 这确实澄清了我的困惑点和测试方法。测试当前失败,因为 SchedulerReportService.submitScheduledReport 需要一个参数,而在测试中我没有提供它。
    猜你喜欢
    • 2017-11-30
    • 2019-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-27
    • 1970-01-01
    • 2017-09-15
    • 2020-08-30
    相关资源
    最近更新 更多