Как написать модульный тест HTTP-макета в финальной версии Angular 2?

#unit-testing #angular

#модульное тестирование #angular

Вопрос:

Я перешел с RC4 на финальную версию (2.1.0) и занимаюсь рефакторингом своих модульных тестов в соответствии с синтаксисом 2.1.0. Это легко, за исключением HTTP-макета.

Я не могу найти никаких примеров того, как имитировать HTTP-запросы в 2.1.0

Вот модульный тест HTTP RC4. Как бы мне переписать это в финальной версии 2.1.0?

 it('ngOnInit()',
  async(inject([TestComponentBuilder, XHRBackend], (tcb:TestComponentBuilder, mockBackend:MockBackend) => {
  tcb.createAsync(Route1ListComponent).then((fix:ComponentFixture<Route1ListComponent>) => {

    // THIS BLOCK OF CODE I NEED HELP TO RE-WRITE TO 2.1.0
    mockBackend.connections.subscribe(
      (connection:MockConnection) => {
        connection.mockRespond(new Response(
          new ResponseOptions({
              body: persons
            }
          )));
      });

    // THIS BLOCK OF CODE WILL NOT CHANGE
    let instance = fix.componentInstance;
    instance.ngOnInit();
    expect(instance.persons.length).toBe(3);
  });
})));
  

ПРИМЕЧАНИЕ: Пожалуйста, НЕ предоставляйте RC-код. Спасибо

Ответ №1:

Первое, что вам нужно сделать, это настроить TestBed . Больше ничего нет TestComponentBuilder . С TestBed помощью, это похоже на настройку @NgModule с нуля, только для тестовой среды. Это означает, что вы добавите тестируемый компонент в declarations , добавите любых поставщиков в provider и любой импорт в imports .

Чтобы настроить макет серверной части для Http provider, вам просто нужно создать Http из MockBackend .

 beforeEach(() => {
  TestBed.configureTestingModule({
    imports: [ HttpModule ],
    declarations: [ RouteListComponent ],
    providers: [
      MockBackend,
      BaseRequestOptions,
      {
        provide: Http,
        useFactory: (backend: MockBackend, options: BaseRequestOptions) => {
          return new Http(backend, options);
        },
        deps: [ MockBackend, BaseRequestOptions ]
      }
    ]
  })
})
  

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

Для теста вы сначала захотите сделать его async тестом, так как в тесте вы будете выполнять асинхронные операции. Это не изменилось с RC, вы просто используете async . Если компонент использует templateUrl (а вы не используете Webpack), то вам нужно будет вызвать TestBed.compileComponents() , в противном случае нет необходимости. После этого вы можете создать компонент с TestBed.createComponent

 let fixture: ComponentFixture<RouteListComponent>;
let component: RouteListComponent;

beforeEach(async(() => {
  TestBed.configureTestingModule({ ... })
  .compileComponents().then(() => {
    fixture = TestBed.createComponent(RouteListComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });
}));

it('...', async(inject([MockBackend], (backend: MockBackend) => {

})))
  

Практически все вышеперечисленное, связанное с тестированием, может быть импортировано из @angular/core/testing . Ваше использование MockBackend все равно будет таким же.

Еще одно замечание, вам не нужно вызывать component.ngOnInit . Это вызывается фреймворком при вызове fixture.detectChanges()

Смотрите также:

Комментарии:

1. Большое спасибо за этот ответ, без него я не смог бы достичь своего решения, изложенного здесь. Мое решение основано на этом, но, правильно или неправильно, избегает использования .. async(inject([MockBackend], (backend: MockBackend) .. этот ответ был высоко оценен … еще раз большое спасибо 🙂 … если у вас есть какие-либо отзывы о моем окончательном решении, это было бы очень полезно … 1

Ответ №2:

Большое спасибо @peeskillet за помощь в получении моего ответа..

 import {APP_BASE_HREF} from '@angular/common';
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {AppModule} from '../../../app.module';
import {persons} from '../../../data/persons';
import {Route1ListComponent} from './route1-list.component';

// HTTP mocking imports
import {BaseRequestOptions, Http, Response, ResponseOptions} from '@angular/http';
import {MockBackend, MockConnection} from '@angular/http/testing';

describe('route1-list.component.ts', () => {

  let fix: ComponentFixture<Route1ListComponent>;
  let instance: Route1ListComponent;
  let injector: any;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [AppModule],
      providers: [{provide: APP_BASE_HREF, useValue: '/'},
        MockBackend,
        BaseRequestOptions,
        {
          provide: Http,
          useFactory: (pBackend: MockBackend, pOptions: BaseRequestOptions) => {
            return new Http(pBackend, pOptions);
          },
          deps: [MockBackend, BaseRequestOptions]
        }]
    }).compileComponents()
      .then(() => {
        fix = TestBed.createComponent(Route1ListComponent);
        instance = fix.componentInstance;
        injector = fix.debugElement.injector;
      });
  }));

  it('should instantiate component', () => {
    expect(instance).toEqual(jasmine.any(Route1ListComponent));
  });

  it('should have expected text', () => {
    let el = fix.debugElement.query(By.css('section.route1-list')).nativeElement;
    expect(el.textContent).toMatch(/route 1 list view/i, 'should have expected text');
  });

  it('ngOnInit()', async(() => {
    let backend = injector.get(MockBackend);
    backend.connections.subscribe(
      (connection: MockConnection) => {
        connection.mockRespond(new Response(
          new ResponseOptions({
              body: persons
            }
          )));
      });

    fix.detectChanges(); // Calls instance.ngOnInit()
    expect(instance.persons.length).toBe(3);
  }));

  it('ngOnInit() failure', async(() => {
    let backend = injector.get(MockBackend);
    backend.connections.subscribe(
      (connection: MockConnection) => {
        connection.mockError(new Error('error'));
      });

    fix.detectChanges(); // Calls instance.ngOnInit()
    expect(instance.persons).toBeUndefined();
  }));

});
  

Обратите внимание, что на момент написания Angular2 docs at ..

https://angular.io/docs/ts/latest/api/http/testing/index/MockBackend-class.html

https://angular.io/docs/ts/latest/api/http/testing/index/MockConnection-class.html

кажется, это неправильно.

Когда я использую Injector.resolveAndCreate , как описано в документах, я получаю сообщение об ошибке:

Свойство ‘resolveAndCreate’ не существует для типа ‘typeof Injector’.

Чтобы исправить это, мне пришлось основывать свой ответ на ответе, предоставленном @peeskillet