[Angular] Test Container component with async provider
2023-09-14 09:00:51 时间
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.
Container component:
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:
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:
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]); }) });
相关文章
- 从 Angular Route 中提前获取数据
- Angular 结合 dygraphs 实现 annotation
- 前端必读3.0:如何在 Angular 中使用SpreadJS实现导入和导出 Excel 文件
- uat环境和生产环境的区别_angular 生产环境 相对路径无效
- 关于 Angular 部署以及 index.html 里 base hRef 属性的关联关系
- Angular 14 inject 函数使用过程中的一些注意事项
- Angular 14 新的 inject 函数介绍
- orbital angular momentum_omnidirectional
- Angular 服务器端渲染场景里,服务器端和客户端渲染出的 HTML 源代码有可能不完全一致