<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                有了公用的多擇組件后我們嘗試使用它打造班級選擇組件。來到course模塊中,并新建KlassMultipleSelect組件。 ``` panjiedeMac-Pro:course panjie$ ng g c KlassMultipleSelect CREATE src/app/course/klass-multiple-select/klass-multiple-select.component.sass (0 bytes) CREATE src/app/course/klass-multiple-select/klass-multiple-select.component.html (36 bytes) CREATE src/app/course/klass-multiple-select/klass-multiple-select.component.spec.ts (721 bytes) CREATE src/app/course/klass-multiple-select/klass-multiple-select.component.ts (328 bytes) UPDATE src/app/course/course.module.ts (478 bytes) ``` V層初始化如下: course/klass-multiple-select/klass-multiple-select.component.html ```html <app-multiple-select [list$]="klasses$" (changed)="onChange($event)"></app-multiple-select> ``` C層初始化如下: course/klass-multiple-select/klass-multiple-select.component.ts ```typescript import { Component, OnInit } from '@angular/core'; import {Observable} from 'rxjs'; import {Klass} from '../../norm/entity/Klass'; @Component({ selector: 'app-klass-multiple-select', templateUrl: './klass-multiple-select.component.html', styleUrls: ['./klass-multiple-select.component.sass'] }) export class KlassMultipleSelectComponent implements OnInit { klasses$: Observable<Klass[]>; constructor() { } ngOnInit() { } onChange($event: Array<Klass>) { } } ``` # 單元測試 我們已經掌握了對嵌套組件的測試的方法,本例中將展示一種更貼近于官方最佳實踐的測試組織方法。以HttpClient為例,angular同時提供了可用于生產環境的HttpClientModule以及用于測試環境的HttpClientTestingModule來做為HttpClient的提供者。官方的這種做法使得在測試過程中引入HttpClient的替身變成一件非常輕松的事情。 反觀我們當前的測試,將相關測試文件統一加入到TestModule快速的解決了測試過程中依賴問題,這本無可厚非,但卻不是一個好的習慣。從簡單的意義上來講,由于并沒有貼近于官言的最佳實踐所以這種模式必然會存在問題,當前沒有發現問題的原因只能是我們對angular理解的還不夠深入,應用的還不夠廣泛;從復雜點的意義上來,在實際的前端開發中團隊需要抽離出如用戶登錄、注銷、權限驗證、菜單生成、AppOnReady等眾多公用服務做為單獨的angular庫在應用到不同的項目中,而在對應的模塊中同步建立測試模塊以提供測試替身則符合angular的規范及習慣,使得團隊其它項目引入公用服務時的單元測試更加規范以提升整體的開發效率。 ## CoreTestingModule 在CoreMoudle中同步建立測試Module ---- CoreTestingModule ``` panjiedeMac-Pro:core panjie$ ng g m CoreTesting CREATE src/app/core/core-testing/core-testing.module.ts (197 bytes) ``` 建立對應的組件替身 ``` panjiedeMac-Pro:core panjie$ cd core-testing/ panjiedeMac-Pro:core-testing panjie$ ng g c MultipleSelect --skip-tests CREATE src/app/core/core-testing/multiple-select/multiple-select.component.sass (0 bytes) CREATE src/app/core/core-testing/multiple-select/multiple-select.component.html (30 bytes) CREATE src/app/core/core-testing/multiple-select/multiple-select.component.ts (305 bytes) UPDATE src/app/core/core-testing/core-testing.module.ts (307 bytes) panjiedeMac-Pro:core-testing panjie$ ``` 參考angular官方的HttpClientTestingModule提供HttpTestingController,提供CoreTestingController ``` panjiedeMac-Pro:core-testing panjie$ ng g class CoreTestingController --skip-tests CREATE src/app/core/core-testing/core-testing-controller.ts (39 bytes) ``` # 一種示例 如何整理CoreTestingModule以及CoreTestingController相信會有千萬種方案,本例給出一種以共生產環境參考: >[warning] 本示例已超出本教程的解釋范圍,是生產環境下組織單元測試文件的一種方法,僅做參考。 在CoreTesting模塊中聲明提供CoreTestingController,以便在單元測試中使用Test.get(CoreTestingController)方法來獲取CoreTestingController: core/core-testing/core-testing.module.ts ```typescript ], providers: [ CoreTestingController ? ] }) export class CoreTestingModule { } ``` * ? 聲明模塊提供CoreTestingController 在測試控制中提供加入、獲取相關單元的功能。 src/app/core/core-testing/core-testing-controller.ts ```typescript /** * 該方案僅適用于在嵌套組件的數量為1. * 由于在get方法中直接以instanceof方法獲取了相關組件 * 所以如果某個組件在被測試組件中多次被引用時 * 只能獲取第一個被push進來的組件 */ export class CoreTestingController { /** * 存儲組件、指令或管道 */ private units = new Array<any>(); constructor() { } /** * 添加單元(組件、指令或管道) * @param unit 單元 */ addUnit(unit: any): void { this.units.push(unit); } /** * 獲取單元(組件、指令或管道) * @param clazz 類型 */ get(clazz: Clazz): any { let result: any = null; this.units.forEach((value) => { if (value.constructor.name === clazz.name) { result = value; } }); return result; } } /** * 定義一個Clazz類型,用于參數中接收 類、接口等 */ export type Clazz = new(...args: any[]) => any; ``` 組件替身聲明與組件具有相同的輸入與輸出,同時將組件本身添加到測試控制器中。 core/core-testing/multiple-select/multiple-select.component.ts ```typescript import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; import {CoreTestingController} from '../core-testing-controller'; import {Observable} from 'rxjs'; @Component({ selector: 'app-multiple-select', templateUrl: './multiple-select.component.html', styleUrls: ['./multiple-select.component.sass'] }) export class MultipleSelectComponent implements OnInit { /** 數據列表 */ @Input() ? list$: Observable<Array<{ name: string }>>; /** 事件彈射器,用戶點選后將最終的結點彈射出去 */ @Output() ? changed = new EventEmitter<Array<any>>(); constructor(private coreTestingController: CoreTestingController?) { this.coreTestingController.addUnit(this); ? } ngOnInit() { } } ``` * ? 聲明與被替組件具有相同的輸入與輸出 * ? 注入測試控制器 * ? 將組件本身加入到測試控制器中 # 小試牛刀 來到course模塊的班級多選組件中進行嵌套組件測試如下: course/klass-multiple-select/klass-multiple-select.component.spec.ts ```typescript beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [KlassMultipleSelectComponent], imports: [ CoreTestingModule ? ] }) .compileComponents(); })); fit('嵌套組件MultipleSelectComponent測試', () => { const coreTestingController = TestBed.get(CoreTestingController); ? const multipleSelect = coreTestingController.get(MultipleSelectComponent)? as MultipleSelectComponent; ? // 斷言input expect(multipleSelect.list$).toBe(component.klasses$); // 斷言output spyOn(component, 'onChange'); const klasses = [new Klass(null, null, null)]; multipleSelect.changed.emit(klasses); expect(component.onChange).toHaveBeenCalledWith(klasses); }); ``` * ? 引入MultipleSelectComponent所在CoreModule對應的測試模塊CoreTestingModule * ? 像angular官方一樣優雅地獲取測試控制器 * ? 像angular官方一樣優雅地獲取被嵌套組件 * ? 此處的MultipleSelectComponent無論是真實的組件還是與組件同名的替身均可正常工作 # 功能開發 班級多選組件中可供選擇的班級來源于數據表klass,為此按MVC的開發理論,首先補充KlassService用于獲取全部的班級列表。 ## service ```javascript panjiedeMac-Pro:service panjie$ ng g s klass CREATE src/app/service/klass.service.spec.ts (328 bytes) CREATE src/app/service/klass.service.ts (134 bytes) ``` 增加all方法來獲取全部的班級數據。由于在前面的章節中并沒有為klass建立單獨的service,而是選擇在klass模塊的index組件中直接向后臺發請的請求。所以此時需要去查看對應組件中獲取全部班級的代碼。最終確認獲取全部方法的接口信息為:`GET http://localhost:8080/Klass?name=`,于是獲取全部班級的代碼如下: service/klass.service.ts ```typescript import {Injectable} from '@angular/core'; import {Observable} from 'rxjs'; import {Klass} from '../norm/entity/Klass'; import {HttpClient, HttpParams} from '@angular/common/http'; @Injectable({ providedIn: 'root' }) export class KlassService { private url = 'http://localhost:8080/Klass'; constructor(private httpClient: HttpClient) { } /** * 獲取所有班級 */ all(): Observable<Klass[]> { const httpParams = new HttpParams().append('name', ''); return this.httpClient.get<Klass[]>(this.url, {params: httpParams}); } } ``` ### 單元測試 service/klass.service.spec.ts ```typescript import {TestBed} from '@angular/core/testing'; import {KlassService} from './klass.service'; import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing'; import {Klass} from '../norm/entity/Klass'; describe('KlassService', () => { beforeEach(() => TestBed.configureTestingModule({ imports: [ HttpClientTestingModule ] })); it('should be created', () => { const service: KlassService = TestBed.get(KlassService); expect(service).toBeTruthy(); }); fit('all', () => { // 數據準備,調用被測方法 const service: KlassService = TestBed.get(KlassService); let result; service.all().subscribe((data) => { result = data; }); // 斷言發起請求符合預期 const testingController: HttpTestingController = TestBed.get(HttpTestingController); const request = testingController.expectOne((req) => req.url === 'http://localhost:8080/Klass'); expect(request.request.headers.has('name')); expect(request.request.method).toEqual('GET'); // 斷言成功的接收到返回值 const klasses = [new Klass(null, null, null)]; request.flush(klasses); expect(result).toBe(klasses); }); }); ``` ## C層 course/klass-multiple-select/klass-multiple-select.component.ts ```typescript import {Component, EventEmitter, OnInit, Output} from '@angular/core'; import {Observable} from 'rxjs'; import {Klass} from '../../norm/entity/Klass'; import {KlassService} from '../../service/klass.service'; @Component({ selector: 'app-klass-multiple-select', templateUrl: './klass-multiple-select.component.html', styleUrls: ['./klass-multiple-select.component.sass'] }) export class KlassMultipleSelectComponent implements OnInit { klasses$: Observable<Klass[]>; @Output() changed = new EventEmitter<Klass[]>(); constructor(private klassService: KlassService) { } ngOnInit() { this.klasses$ = this.klassService.all(); } onChange($event: Array<Klass>) { this.changed.emit($event); } } ``` ### 單元測試 該組件依賴于KlassService,為此在進行單元測試前先建立KlassService的測試替身KlassStubService ``` panjiedeMac-Pro:service panjie$ ng g s KlassStub --skip-tests CREATE src/app/service/klass-stub.service.ts (138 bytes) ``` 在替身中同樣創建all方法。 service/klass-stub.service.ts ```typescript import {Observable} from 'rxjs'; import {Klass} from '../norm/entity/Klass'; export class KlassStubService { constructor() { } all(): Observable<Klass[]> { return null; } } ``` 補充班級選擇組件ngOnInit方法及changed方法的測試: course/klass-multiple-select/klass-multiple-select.component.spec.ts ```typescript providers: [ {provide: KlassService, useClass: KlassStubService} ] fit('onChange', () => { let result; component.changed.subscribe((data) => { result = data; }); const klasses = [new Klass(null, null, null)]; component.onChange(klasses); expect(result).toBe(klasses); }); fit('ngOnInit', () => { const klassService: KlassService = TestBed.get(KlassService); const klasses$ = of([new Klass(null, null, null)]); spyOn(klassService, 'all').and.returnValue(klasses$); component.ngOnInit(); expect(component.klasses$).toBe(klasses$); }); ``` 單元測試通過,本節完成: ![](https://img.kancloud.cn/fe/29/fe296d7a079ea45df6500f79435df3ea_320x136.png) # 本節小測 請參考本節中的測試示例,請嘗試在course文件夾中建立CourseModule對應的測試CourseTestingModule以及相關文件。 # 參考文檔 | 名稱 | 鏈接 | 預計學習時長(分) | | --- | --- | --- | | 源碼地址 | [https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step6.1.4](https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step6.1.4) | - |
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看