Неожиданный toHaveBeenCalled на catchError (RxJS) - PullRequest
0 голосов
/ 18 октября 2018

Я использую приложение Angular 6 Tour of Heroes и пытаюсь написать модульные тесты для HeroService.getHeroes().

HeroService определяется как:

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

import { Observable, of } from 'rxjs';
import { catchError, map, tap } from 'rxjs/operators';

import { Hero } from './hero';
import { MessageService } from './message.service';

const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};

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

  private heroesUrl = 'api/heroes';  // URL to web api

  constructor(
    private http: HttpClient,
    private messageService: MessageService) { }

  /** GET heroes from the server */
  getHeroes (): Observable<Hero[]> {
    return this.http.get<Hero[]>(this.heroesUrl)
      .pipe(
        tap(heroes => this.log('fetched heroes')),
        catchError(this.handleError('getHeroes', []))
      );
  }

 ...

  /**
   * 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);
    };
  }

  /** Log a HeroService message with the MessageService */
  private log(message: string) {
    this.messageService.add(`HeroService: ${message}`);
  }
}

Мои модульные тесты:

import { TestBed, inject } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';

import { HeroService } from './hero.service';
import { MessageService } from './message.service';
import { Hero } from './hero';

const mockData = [
  { id: 1, name: 'Hulk' },
  { id: 2, name: 'Thor' },
  { id: 3, name: 'Iron Man' }
] as Hero[];

describe('Hero Service', () => {

  let heroService;
  let httpTestingController: HttpTestingController;

  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);
  });

  afterEach(() => {
    httpTestingController.verify();
  });

  it('should be created', () => {
    expect(heroService).toBeTruthy();
  });

  describe('getHeroes', () => {

    it('should return mock heroes', () => {
      spyOn(heroService, 'handleError');
      spyOn(heroService, 'log');

      heroService.getHeroes().subscribe(
        heroes => expect(heroes.length).toEqual(this.mockHeroes.length),
        fail
      );

      const req = httpTestingController.expectOne(heroService.heroesUrl);
      expect(req.request.method).toEqual('GET');
      req.flush(this.mockHeroes);

      expect(heroService.handleError).not.toHaveBeenCalled();
      expect(heroService.log).toHaveBeenCalledTimes(1);
    });
  });
});

Тесты не выполняются с:

Error message from Karma

Сбой является неожиданным, так как кажется, что действительно вызывается HeroService.handleError, что не относится к тестам.Почему во время выполнения теста вызывается HeroService.handleError и как мне исправить свои юнит-тесты?

1 Ответ

0 голосов
/ 19 октября 2018

Вы пишете тесты немного неправильно, тест должен быть похож на это (обратите внимание на шпионские ссылки logSpy и errorSpy ).

it('should return mock heroes', () => {
  const errorSpy = spyOn(heroService, 'handleError').and.callThrough();
  const logSpy = spyOn(heroService, 'log');

  heroService.getHeroes().subscribe(
    (heroes: Hero[]) => {
      expect(heroes.length).toEqual(this.mockHeroes.length);
    }
  );

  const req = httpTestingController.expectOne(heroService.heroesUrl);
  //console.log(req);
  expect(req.request.method).toEqual('GET');
  req.flush(this.mockHeroes);

  expect(errorSpy).not.toHaveBeenCalled();
  expect(logSpy).toHaveBeenCalledTimes(1);
});

Вот stackblitz показ теста в действии, НАСЛАЖДАЙТЕСЬ!

...