刚开始为 Angular 的英雄之旅编写测试用例,但是当我运行它们时,我得到了一个ERROR in src/app/hero.service.spec.ts:36:5 - error TS2532: Object is possibly 'undefined
. 我对角度很陌生,我正在努力找出问题所在。我查看了我在网上找到的几个实现,但它们与我的代码相同。任何帮助表示赞赏。
终端输出
ng test --code-coverage
10% building 2/2 modules 0 active11 09 2020 16:07:32.845:WARN [karma]: No captured browser, open http://localhost:9876/
11 09 2020 16:07:32.850:INFO [karma-server]: Karma v5.0.9 server started at http://0.0.0.0:9876/
11 09 2020 16:07:32.852:INFO [launcher]: Launching browsers Chrome with concurrency unlimited
11 09 2020 16:07:32.899:INFO [launcher]: Starting browser Chrome
ERROR in src/app/hero.service.spec.ts:36:5 - error TS2532: Object is possibly 'undefined'.
36 this.mockHeroes = [...mockData];
~~~~
src/app/hero.service.spec.ts:37:5 - error TS2532: Object is possibly 'undefined'.
37 this.mockHero = this.mockHeroes[0];
~~~~
src/app/hero.service.spec.ts:37:21 - error TS2532: Object is possibly 'undefined'.
37 this.mockHero = this.mockHeroes[0];
~~~~
src/app/hero.service.spec.ts:38:5 - error TS2532: Object is possibly 'undefined'.
38 this.mockId = this.mockHero.id;
~~~~
src/app/hero.service.spec.ts:38:19 - error TS2532: Object is possibly 'undefined'.
38 this.mockId = this.mockHero.id;
hero.service.spec.ts
import { TestBed, inject } from '@angular/core/testing';
import { HttpClientModule, HttpClient, HttpResponse } from '@angular/common/http';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { MessageService } from './message.service';
import { HeroService } from './hero.service';
import { Hero } from './hero';
//mock data to be used when testing
const mockData = [
{ id: 1, name: 'Hulk' },
{ id: 2, name: 'Thor'},
{ id: 3, name: 'Iron Man'}
] as Hero[];
describe('HeroService', () => {
let heroService;
let messageService: MessageService;
let httpClient: HttpClient;
let service;
let httpTestingController: HttpTestingController;
//before each test, configure a new test med as if it was the hero service
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpClientTestingModule
],
providers: [HeroService, MessageService]
});
httpTestingController = TestBed.get(HttpTestingController);
this.mockHeroes = [...mockData];
this.mockHero = this.mockHeroes[0];
this.mockId = this.mockHero.id;
heroService = TestBed.get(HeroService);
});
//no pending http requests
afterEach(() => {
httpTestingController.verify();
});
//test, service should be able to be started
it('should exist', () => {
expect(heroService).toBeTruthy();
})
});
hero.service.ts(以防问题出在此处)
import { MessageService } from './message.service';
import { HEROES } from './mock-heroes';
import { Injectable } from '@angular/core';
import { Hero } from './hero';
import { Observable, of } from 'rxjs';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { catchError, map, tap } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class HeroService {
myHero: string = 'hero';
private heroesUrl = 'api/heroes'; // URL to web api
httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
constructor(
private http: HttpClient,
private MessageService: MessageService) { }
getHeroes(): Observable<Hero[]> {
this.MessageService.add(`HeroService: Fetched Heroes`);
return this.http.get<Hero[]>(this.heroesUrl)
.pipe(
tap(_ => this.log('fetched heroes')),
catchError(this.handleError<Hero[]>('getHeroes', []))
)
}
getString() {
return this.myHero;
}
getHero(id: number): Observable<Hero> {
const url = `${this.heroesUrl}/${id}`;
return this.http.get<Hero>(url).pipe(
tap(_ => this.log(`fetched hero id=${id}`)),
catchError(this.handleError<Hero>(`getHero id=${id}`))
);
}
/** Log a HeroService message with the MessageService */
private log(message: string) {
this.MessageService.add(`HeroService: ${message}`);
}
/**
* Handle Http operation that failed.
* Let the app continue.
* @param operation - name of the operation that failed
* @param result - optional value to return as the observable result
*/
private handleError<T>(operation = 'operation', result?: T) {
return (error: any): Observable<T> => {
// TODO: send the error to remote logging infrastructure
console.error(error); // log to console instead
// TODO: better job of transforming error for user consumption
this.log(`${operation} failed: ${error.message}`);
// Let the app keep running by returning an empty result.
return of(result as T);
};
}
updateHero(hero: Hero): Observable<any> {
return this.http.put(this.heroesUrl, hero, this.httpOptions).pipe(
tap(_ => this.log(`updated hero id=${hero.id}`)),
catchError(this.handleError<any>("updateHero"))
);
}
addHero(hero: Hero): Observable<Hero> {
return this.http.post<Hero>(this.heroesUrl, hero, this.httpOptions).pipe(
tap((newHero: Hero) => this.log(`added hero w /id=${newHero.id}`)),
catchError(this.handleError<Hero>('addHero'))
);
}
deleteHero(hero: Hero | number): Observable<Hero> {
const id = typeof hero === 'number' ? hero : hero.id;
const url = `${this.heroesUrl}/${id}`;
return this.http.delete<Hero>(url, this.httpOptions).pipe(
tap(_ => this.log(`deleted hero id=${id}`)),
catchError(this.handleError<Hero>('deleteHero'))
);
}
searchHeroes(term: string): Observable<Hero[]>{
if(!term.trim()) {
return of([]);
}
return this.http.get<Hero[]>(`${this.heroesUrl}/?name=${term}`).pipe(
tap(x => x.length ?
this.log(`found heroes matching "${term}"`) :
this.log(`no heroes matching "${term}"`)),
catchError(this.handleError<Hero[]>('searchHeroes', []))
)
};
}