Как протестировать обработку ошибок компонента с помощью наблюдаемого сервиса

Контекст

Я пытаюсь протестировать компонент. В другом посте я спросил о тестировании прямого вызова функции. Здесь я сосредотачиваюсь, используя тот же компонент примера, на обработке ошибок моего компонента.

Я хочу проверить, когда моя служба возвращает наблюдаемую ошибку, что мой компонент правильно вызывает console.error (error).

Как "создать" этот триггер ошибки и проверить, правильно ли мой компонент обрабатывает его. Я слышал, что это могут делать шпионы, но не знаю, где установить этого шпиона.

Я хочу смоделировать случай, когда HTTP-запрос службы может завершиться ошибкой. По любой причине или по любому коду ошибки.

Код

Вот код для компонента, службы-заглушки и моих файлов спецификаций.

Компонент

import { Component, OnInit, ViewContainerRef } from '@angular/core';

import { UserManagementService } from '../../shared/services/global.api';
import { UserListItemComponent } from './user-list-item.component';

@Component({
  selector: 'app-user-list',
  templateUrl: './user-list.component.html'
})
export class UserListComponent implements OnInit {
  public userList = [];

  constructor(
    private _userManagementService: UserManagementService,    
  ) { }

  ngOnInit() {
    this.getUserList();
  }

  onRefreshUserList() {
    this.getUserList();
  }

  getUserList(notifyWhenComplete = false) {
    this._userManagementService.getListUsers().subscribe(
      result => {
        this.userList = result.objects;
      },
      error => {
        console.error(error); // That's the part of my component I want to test     
      },
      () => {
        if (notifyWhenComplete) {
          console.info('Notification');
        }
      }
    );
  }
}

Компонентный файл спецификации

import { NO_ERRORS_SCHEMA } from '@angular/core';
import {
  async,
  fakeAsync,
  ComponentFixture,
  TestBed,
  tick,
  inject
} from '@angular/core/testing';

import { Observable } from 'rxjs/Observable';

// Components
import { UserListComponent } from './user-list.component';

// Services
import { UserManagementService } from '../../shared/services/global.api';
import { UserManagementServiceStub } from '../../testing/services/global.api.stub';

let comp:    UserListComponent;
let fixture: ComponentFixture<UserListComponent>;
let service: UserManagementService;

describe('UserListComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [UserListComponent],
      imports: [],
      providers: [
        {
          provide: UserManagementService,
          useClass: UserManagementServiceStub
        }
      ],
      schemas: [ NO_ERRORS_SCHEMA ]
    })
    .compileComponents();
  }));

  tests();
});

function tests() {
  beforeEach(() => {
    fixture = TestBed.createComponent(UserListComponent);
    comp = fixture.componentInstance;

    service = TestBed.get(UserManagementService);
  });

  it(`should be initialized`, () => {
    expect(fixture).toBeDefined();
    expect(comp).toBeDefined();
  });

  it(`should get the user List after ngOnInit`, async(() => {
    fixture.detectChanges();

    expect(comp.userList.length).toBe(3, 'user list exists after init');
  }));
}

Служба заглушки

import { Observable } from 'rxjs/Observable';

export class UserManagementServiceStub {
  getListUsers() {
    return Observable.from([      
      {
        count: 3, 
        objects: 
        [
          {
            id: "7f5a6610-f59b-4cd7-b649-1ea3cf72347f",
            name: "user 1",
            group: "any"
          },
          {
            id: "d6f54c29-810e-43d8-8083-0712d1c412a3",
            name: "user 2",
            group: "any"
          },
          {
            id: "2874f506-009a-4af8-8ca5-f6e6ba1824cb", 
            name: "user 3",
            group: "any"
          }
        ]
      }
    ]);
  }
}

person BlackHoleGalaxy    schedule 09.04.2017    source источник
comment
Контекст тот же, но вопрос в другом. Я не хочу путать два типа вопросов в одном посте. Если я помещаю все в один пост с двумя вопросами, а люди отвечают только на один вопрос / проблему, как отметить правильный ответ как принятый ...   -  person BlackHoleGalaxy    schedule 09.04.2017
comment
Ни дубликата звонков на шпионский пост. Итого разные проблемы.   -  person BlackHoleGalaxy    schedule 09.04.2017


Ответы (1)


Если я правильно понимаю, что вам просто нужен способ вернуть ошибку из зарезервированной службы, вы можете просто создать отдельную заглушку, которая возвращает наблюдаемую ошибку, а затем использовать ее в тесте, который проверяет обработку ошибок:

export class UserManagementServiceErrorStub {
  getListUsers() {
    return Observable.throw(new Error('Test error'));
  }
}

Вот действительно хорошая статья о тестировании сервисов Angular с использованием Observables: https://web.archive.org/web/20191214083526/http://www.zackarychapple.guru/angular2/2016/11/25/angular2-testing-services.html

Обновление - 06.01.2019 для RxJS 6

import { throwError } from 'rxjs'; 

class UserManagementServiceErrorStub {
  getListUsers() {
    return throwError(new Error('Test error'));
  }
}
person Steve Land    schedule 17.04.2017
comment
Для последней версии rxjs он throwError импортирован из rxjs, а не Observable.throw - person Henry Blyth; 21.05.2019
comment
Ссылка на статью, похоже, была взломана - person vonec; 11.05.2021
comment
спасибо @vonec - обновлен кэшированной версией оригинала waybackmachine - person Steve Land; 11.05.2021