首页 > 解决方案 > TS2532:在测试 Angular 的英雄之旅时,对象可能“未定义”

问题描述

刚开始为 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', []))
    )
  };
}

标签: angulartypescriptunit-testingrxjsangular-tour-of-heroes

解决方案


删除以下行应该可以在不改变测试行为的情况下修复错误

this.mockHeroes = [...mockData];
this.mockHero = this.mockHeroes[0];
this.mockId = this.mockHero.id;

推荐阅读