我尝试通过一些示例第一次使用Jasmine进行角度单元测试,但发现了一些问题。
因此,我有这个People List Component类来实现组件的逻辑:
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()方法,该方法目前仅返回1(我想尽可能简化所有方法)。我的第一个单元测试必须测试此方法,只需检查返回值是否为1。
如您所见,前面的方法将EventService服务实例注入到构造函数中。
这是EventSerive类代码:
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工具不是那么聪明。
eventServiceSpy
is of typejasmine.SpyObj<EventService>;
. According to typing the methodTestBed.inject(EventService);
is expected to return object of typeEventService
while with Angular DI it is possible to provide any object for any injection token (in the case provide the injection token is classEventService
). Above you provided the object of properjasmine.SpyObj<EventService>
:So now the method
TestBed.inject(EventService);
would provide object of typejasmine.SpyObj<EventService>
. But development tools are not that smart enough so far to handle typing here properly. A workaround would be casting: