Testowanie elementów z zależnościami

W poprzednim przykładzie testowaliśmy prosty serwis LocalStoragePrefixService, który nie korzystał z narzędzi dostarczanych przez framework Angular - mogliśmy testować go w całkowitej izolacji. Co w momencie, gdy nasz moduł jednak z takich narzędzi korzysta?

Przykładowy serwis korzystający z wbudowanego serwisu do wykonywania zapytań HTTP:

import { Observable } from 'rxjs/Observable';
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';

import "rxjs/add/operator/map";

const BASE_API_URL = 'https://api.punkapi.com/v2/';

@Injectable()
export class RestClientService {

  constructor (
    private _http: Http
  ) { }

  public get (url): Observable<any> {
    return this._http
      .get(`${BASE_API_URL}${url}`)
      .map((res: Response) => res.json());
  }
}

Aby móc przetestować należy skorzystać ze specjalnej klasy MockBackend zastępującej XHRBackend jak w poniższym przykładzie:

import { TestBed, inject} from '@angular/core/testing';
import { HttpModule, Response, ResponseOptions, XHRBackend } from '@angular/http';
import { MockBackend } from '@angular/http/testing';
import { RestClientService } from './rest-client.service';

describe('RestClientService', () => {
  beforeEach(() => {

    TestBed.configureTestingModule({
      imports: [HttpModule],
      providers: [
        RestClientService,
        {provide: XHRBackend, useClass: MockBackend},
      ]
    });
  });

  describe('get()', () => {

    it('should return an Observable of objects',
      inject([RestClientService, XHRBackend], (restClient, mockBackend) => {

        const mockResponse = [
          {
            id: 185,
            name: "Tactical Nuclear Penguin",
            brewers_tips: "This level of alcohol can be achieved using a domestic freezer. Use a container with a tap close to the bottom so you can run the un-frozen, concentrated beer from under the ice on top. You may have to do this three or four times.",
            contributed_by: "Sam Mason <samjbmason>"
          },
          {
            id: 186,
            name: "Jasmine IPA",
            brewers_tips: "When dry hopping with the jasmine, use a muslin or cloth like a tea bag, and make sure it has a heavy object in it (ensure its clean). This will help to keep the jasmine submerged in the beer for better flavour extraction.",
            contributed_by: "Sam Mason <samjbmason>"
          }
        ];

        mockBackend.connections.subscribe((connection) => {
          connection.mockRespond(new Response(new ResponseOptions({
            body: JSON.stringify(mockResponse)
          })));
        });

        restClient.get().subscribe((beers) => {
          expect(beers.length).toBe(2);

          expect(beers[0].id).toBe(185);
          expect(beers[0].name).toBe('Tactical Nuclear Penguin');

          expect(beers[1].id).toBe(186);
          expect(beers[1].name).toBe('Jasmine IPA');
        });
      }));
  });
});

Testowanie komponentów z zależnościami

Jeżeli posiadamy komponent, którego zależnością jest serwis w większości przypadków będziemy musieli podmienić realny obiekt na jego imitacje.

Przykładowy komponent posiadający opisaną zależność:

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

import { Beer } from '../../shared/model/beer';
import { BeerService } from '../../shared/service/beer.service';


@Component({
  selector: 'app-random-beer',
  templateUrl: './random-beer.component.html',
  styleUrls: ['./random-beer.component.css']
})
export class RandomBeerComponent implements OnInit {

  public beer: Beer;

  constructor (
    private _beer: BeerService
  ) {}

  ngOnInit () {
    this._beer.getRandomBeer().subscribe(beer => {
      this.beer = beer;
    });
  }

}

Test z użyciem imitacji serwisu BeerService wyglądać może następująco:

import {async, ComponentFixture, TestBed} from '@angular/core/testing';

import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';

import {RandomBeerComponent} from './random-beer.component';
import {BeerService} from '../../shared/service/beer.service';

import "rxjs/add/observable/of";
import {Observable} from "rxjs/Observable";
import {Beer} from "../../shared/model/beer";
import {RestClientService} from "../../shared/service/rest-client/rest-client.service";
import {MockBackend} from '@angular/http/testing';
import {Http} from "@angular/http";

class MockBeerService extends BeerService {
  public getRandomBeer(): Observable<Beer> {
    return Observable.of({});
  }
}

describe('RandomBeerComponent', () => {
  let component: RandomBeerComponent;
  let fixture: ComponentFixture<RandomBeerComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [RandomBeerComponent],
      schemas: [CUSTOM_ELEMENTS_SCHEMA],
      providers: [
        {provide: BeerService, useClass: MockBeerService},
        RestClientService,
        {provide: Http, useClass: MockBackend}
      ]
    })
      .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(RandomBeerComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });
  it('should be created', () => {
    expect(component).toBeTruthy();
  });
});

results matching ""

    No results matching ""