[Angular] Test Container component with async provider

The main idea for testing contianer component is to make sure it setup everythings correctlly. Call the onInit() lifecycle first, then the variables have the right value. Methods will be called with the right params.css

 

Container component:json

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormArray, FormGroup } from '@angular/forms';

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/forkJoin';

import { Product, Item } from '../../models/product.interface';

import { StockInventoryService } from '../../services/stock-inventory.service';

@Component({
  selector: 'stock-inventory',
  styleUrls: ['stock-inventory.component.scss'],
  template: `
    <div class="stock-inventory">
      <form [formGroup]="form" (ngSubmit)="onSubmit()">
        
        <stock-branch
          [parent]="form">
        </stock-branch>

        <stock-selector
          [parent]="form"
          [products]="products"
          (added)="addStock($event)">
        </stock-selector>

        <stock-products 
          [parent]="form"
          [map]="productsMap"
          (remove)="removeStock($event, i)">
        </stock-products>

        <div class="stock-inventory__buttons">
          <button 
            type="submit" 
            [disabled]="form.invalid">
            Order stock
          </button>
        </div>

        <pre>{{ form.value | json }}</pre>

      </form>
    </div>
  `
})
export class StockInventoryComponent implements OnInit {

  products: Product[];

  productsMap: Map<number, Product>;

  form = this.fb.group({
    store: this.fb.group({
      branch: '',
      code: ''
    }),
    selector: this.createStock({}),
    stock: this.fb.array([])
  });

  constructor(
    private fb: FormBuilder,
    private stockService: StockInventoryService
  ) {}

  ngOnInit() {

    const cart = this.stockService.getCartItems();
    const products = this.stockService.getProducts();

    Observable
      .forkJoin(cart, products)
      .subscribe(([cart, products]: [Item[], Product[]]) => {

        const mapInfo = products.map<[number, Product]>(product => [product.id, product]);
        this.products = products;
        this.productsMap = new Map<number, Product>(mapInfo);
        cart.forEach(item => this.addStock(item));

      });
  }

  createStock(stock) {
    return this.fb.group({
      product_id: (parseInt(stock.product_id, 10) || ''),
      quantity: (stock.quantity || 10)
    });
  }

  addStock(stock) {
    const control = this.form.get('stock') as FormArray;
    control.push(this.createStock(stock));
  }

  removeStock({ group, index }: { group: FormGroup, index: number }) {
    const control = this.form.get('stock') as FormArray;
    control.removeAt(index);
  }

  onSubmit() {
    console.log('Submit:', this.form.value);
  }
}

 

Service:api

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

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';

import { Product, Item } from '../models/product.interface';

@Injectable()
export class StockInventoryService {
  
  constructor(private http: Http) {}

  getCartItems(): Observable<Item[]> {
    return this.http
      .get('/api/cart')
      .map((response: Response) => response.json())
      .catch((error: any) => Observable.throw(error.json()));
  }

  getProducts(): Observable<Product[]> {
    return this.http
      .get('/api/products')
      .map((response: Response) => response.json())
      .catch((error: any) => Observable.throw(error.json()));
  }

}

 

Test:ide

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
import { DebugElement } from '@angular/core';

import { ReactiveFormsModule } from '@angular/forms';

import { StockInventoryComponent } from './stock-inventory.component';
import { StockBranchComponent } from '../../components/stock-branch/stock-branch.component';
import { StockCounterComponent } from '../../components/stock-counter/stock-counter.component';
import { StockProductsComponent } from '../../components/stock-products/stock-products.component';
import { StockSelectorComponent } from '../../components/stock-selector/stock-selector.component';
import { StockInventoryService } from '../../services/stock-inventory.service';

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';

const products = [{ id: 1, price: 10, name: 'Test' }, { id: 2, price: 100, name: 'Another test'}];
const items = [{ product_id: 1, quantity: 10 }, { product_id: 2, quantity: 5 }];

TestBed.initTestEnvironment(
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting()
);

class MockStockInventoryService {
  getProducts() {
    return Observable.of(products);
  }
  getCartItems() {
    return Observable.of(items);
  }
}

describe('StockInventoryComponent', () => {

  let component: StockInventoryComponent;
  let fixture: ComponentFixture<StockInventoryComponent>;
  let el: DebugElement;
  let service: StockInventoryService;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        ReactiveFormsModule
      ],
      declarations: [
        StockBranchComponent,
        StockCounterComponent,
        StockProductsComponent,
        StockSelectorComponent,
        StockInventoryComponent
      ],
      providers: [
        {provide: StockInventoryService, useClass: MockStockInventoryService }
      ]
    })

    fixture = TestBed.createComponent(StockInventoryComponent)
    component = fixture.componentInstance;
    el = fixture.debugElement;
    service = el.injector.get(StockInventoryService)
  })

  it('should call through tow service funs when init', () => {
    spyOn(service, 'getCartItems').and.callThrough();
    spyOn(service, 'getProducts').and.callThrough();
    component.ngOnInit();
    expect(service.getCartItems).toHaveBeenCalled();
    expect(service.getProducts).toHaveBeenCalled();
  })

  it('should store the response into products', () => {
    component.ngOnInit();
    expect(component.products).toEqual(products)
  })

  it('should set producetsMap', () => {
    component.ngOnInit();
    expect(component.productsMap.get(1)).toEqual(products[0]);
    expect(component.productsMap.get(2)).toEqual(products[1]);
  })

  it('should call addStock with the right param', () => {
    spyOn(component, 'addStock');
    component.ngOnInit();
    expect(component.addStock).toHaveBeenCalledWith(items[0]);
    expect(component.addStock).toHaveBeenCalledWith(items[1]);
  })
});
相關文章
相關標籤/搜索