为什么我在 Jasmine 单元测试中遇到此错误?

Why am I obtaining this error on my Jasmine unit test?

我正在尝试使用 Jasmine 实现 Angular 单元测试,这是我第一次在一些示例之后发现一些问题。

所以我有这个 PeopleListComponent class 实现组件的逻辑:

    import { Component, OnInit, ElementRef, ViewChild } from '@angular/core';
    import { EventService } from '../event.service';
    import interactionPlugin, { Draggable } from '@fullcalendar/interaction';

    interface WorkShiftTypes {
      name: string;
      value: string;
    }

    @Component({
      selector: 'app-people-list',
      templateUrl: './people-list.component.html',
      styleUrls: ['./people-list.component.css']
    })
    export class PeopleListComponent implements OnInit {

      people: any[];

      //cities: City[];

      workShiftTypes: WorkShiftTypes[];
      selectedShift: WorkShiftTypes;

      @ViewChild('draggable_people') draggablePeopleExternalElement: ElementRef;

      constructor(private eventService: EventService) { }

      ngOnInit(): void {
        this.eventService.getPeople().then(people => {this.people = people;});

        this.selectedShift = {name: 'Mattina', value: 'Mattina'};

        this.workShiftTypes = [
          {name: 'Mattina', value: 'Mattina'},
          {name: 'Pomeriggio', value: 'Pomeriggio'},
          {name: 'Notte', value: 'Notte'},
          {name: 'Custom', value: 'Custom'}
        ];
      }

      ngAfterViewInit() {
        console.log("PEOPLE LIST ngAfterViewInit() START !!!")
        var self = this

        new Draggable(this.draggablePeopleExternalElement.nativeElement, {
          itemSelector: '.fc-event',
          eventData: function(eventEl) {
            console.log("DRAG !!!");
            //console.log("SELECTED SHIFT: " + self.selectedShift.value);
            return {
              title: eventEl.innerText,
              startTime: "17:00",
              duration: { hours: 8 }
            };
          }
        });

      }

      createEventObject() {
        return 1;
      }

    }

如您所见,它包含这个非常简单的 createEventObject() 方法,目前只有 return 1(我想尽可能简单).我的第一个单元测试必须测试此方法,只需检查 returned 值为 1.

如您所见,前面的方法在构造函数中注入了一个 EventService 服务实例。

这是 EventSerive class 代码:

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

    @Injectable()
    export class EventService {

      private events = [
        {id: 1, title: 'All Day Event', start: '2017-02-01'},
        {id: 2, title: 'Long Event', start: '2017-02-07', end: '2017-02-10'},
        {id: 3, title: 'Repeating Event', start: '2017-02-09T16:00:00'},
      ];

      private people = [
        {id: 1, name: "PERSONA 1"},
        {id: 2, name: "PERSONA 2"},
        {id: 3, name: "PERSONA 3"},
        {id: 4, name: "PERSONA 4"},
        {id: 5, name: "PERSONA 5"},
      ]


      constructor(private http: HttpClient) {}

      /*
      getEvents(): Promise<any[]> {
        return this.http.get('assets/json_mock/calendarevents.json')
          .toPromise()
          .then(res => JSON.parse(JSON.stringify(res)).data)
          .then(res => {
            console.log(res);
            // you returned no value here!
            return res;
          })
      }
      */

    getEvents(): Promise<any[]> {
      return Promise.all(this.events)
      .then(res => {
        console.log(res);
        // you returned no value here!
        return res;
      })
    }

    addEvent(event) {
      //this.events.push(event);
      //console.log("EVENT:")
      //console.log(event.event.title);
      console.log(event.event.start);
      console.log(event);
      const newEvent = {id: 5, title: event.event.title, start: event.event.start, end: event.event.end};
      this.events.push(newEvent);

      console.log(this.events);

    }

      getPeople(): Promise<any[]> {
        return Promise.all(this.people)
            .then(res => {
              console.log(res);
              return res;
            })
      }

    }

如您所见,此方法本身将另一个对象注入构造函数(HttpClient 以执行 HTTP 请求)。

好的,现在我想在我的单元测试中实现 createEventObject() 方法测试。所以我有这个 people-list.component.spec.ts 文件:

    import {async, ComponentFixture, TestBed} from '@angular/core/testing';
    import { PeopleListComponent } from "./people-list.component"
    import { EventService } from '../event.service';
    import {HttpClientTestingModule} from '@angular/common/http/testing';

    describe('people-list', () => {
        let component: PeopleListComponent;
        let fixture: ComponentFixture<PeopleListComponent>;
        let eventServiceSpy : jasmine.SpyObj<EventService>;
            beforeEach(async(() => {
              const eventServiceSpyObj = jasmine.createSpyObj('EventService',['getPeople'])

              TestBed.configureTestingModule({
                  declarations: [PeopleListComponent],
                  imports: [HttpClientTestingModule],
                  providers : [{ provide : EventService, useValue : eventServiceSpyObj }]

            });

            // Create a testing version of my PeopleListComponent:
            fixture = TestBed.createComponent(PeopleListComponent);
            eventServiceSpy = TestBed.inject(EventService);
            component = fixture.componentInstance;
            fixture.detectChanges();
        }));

        it('createEventObject()  return 1', () => {
            expect(component.createEventObject()).toBe(1)
        })
    })

我完全不确定它在逻辑上是否正确...

问题是目前IDE在这一行给我一个错误:

eventServiceSpy = TestBed.inject(EventService);

错误是:

Type 'EventService' is not assignable to type 'SpyObj<EventService>'.
  Type 'EventService' is not assignable to type '{ getEvents: (() => Promise<any[]>) & Spy<() => Promise<any[]>>; addEvent: ((event: any) => void) & Spy<(event: any) => void>; getPeople: (() => Promise<any[]>) & Spy<...>; }'.
    Types of property 'getEvents' are incompatible.
      Type '() => Promise<any[]>' is not assignable to type '(() => Promise<any[]>) & Spy<() => Promise<any[]>>'.
        Type '() => Promise<any[]>' is missing the following properties from type 'Spy<() => Promise<any[]>>': and, calls, withArgsts(2322)

怎么了?如何正确测试我的组件方法?

TypeScript 工具没那么聪明。

let eventServiceSpy : jasmine.SpyObj<EventService>;
eventServiceSpy = TestBed.inject(EventService);

eventServiceSpyjasmine.SpyObj<EventService>; 类型。根据键入方法 TestBed.inject(EventService); 预计 return 类型 EventService 的对象,而使用 Angular DI 可以为任何注入令牌提供任何对象(在这种情况下注入令牌是 class EventService)。上面你提供了 jasmine.SpyObj<EventService>:

类型的对象
providers : [{ provide : EventService, useValue : eventServiceSpyObj }

所以现在方法 TestBed.inject(EventService); 将提供类型 jasmine.SpyObj<EventService> 的对象。但是到目前为止,开发工具还不够智能,无法正确处理此处的输入。 解决方法是铸造:

eventServiceSpy = <jasmine.SpyObj<EventService>><any>TestBed.inject(EventService);