<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>

                同其它組件一樣,首先我們初始化原型: ```bash panjiedeMacBook-Pro:clazz panjie$ ng g c edit CREATE src/app/clazz/edit/edit.component.css (0 bytes) CREATE src/app/clazz/edit/edit.component.html (19 bytes) CREATE src/app/clazz/edit/edit.component.spec.ts (612 bytes) CREATE src/app/clazz/edit/edit.component.ts (267 bytes) UPDATE src/app/clazz/clazz.module.ts (663 bytes) ``` ## V層 班級新建組件中引入了教師選擇列表組件,此時在原型初始化中我們仍然引用該組件以達到快速初始化原型的目的: ```html <form class="container-sm"> <div class="mb-3 row"> <label class="col-sm-2 col-form-label">名稱</label> <div class="col-sm-10"> <input type="text" class="form-control"> <small class="text-danger"> 班級名稱不能為空 </small> </div> </div> <div class="mb-3 row"> <label class="col-sm-2 col-form-label">班主任</label> <div class="col-sm-10"> <app-klass-select></app-klass-select> <small class="text-danger"> 必須指定一個班主任 </small> </div> </div> <div class="mb-3 row"> <div class="col-sm-10 offset-2"> <button class="btn btn-primary">保存 </button> </div> </div> </form> ``` 然后在`src/app/clazz/edit/edit.component.spec.ts`下的動態測試模塊中加入`app-klass-select`對應的組件`KlassSelectComponent`: ```typescript await TestBed.configureTestingModule({ declarations: [EditComponent, KlassSelectComponent] }) ``` 最后在單元測試的測試用例上加入`fit`,啟用`ng t`來查看原型: ![image-20210401150902012](https://img.kancloud.cn/a0/8f/a08fe878473e4f0f5b2e8e97b735f8da_1608x154.png) 由于`KlassSelectComponent`依賴于后臺的數據,在未引入相關的模塊和MockApi攔截器前,進行單元測試時將觸發上述錯誤。該錯誤在教師新增組件的測試中已經出現過一次,請嘗試自行解決。 ## 圖解原理 隨著項目的增大、組件間的關系將會逐漸加強。這將會使我們的測試變得越發不可控制。假設A組件當前僅僅依賴于`AMockApi`,接著我們又開發了100個依賴于組件A的新組件,在測試時我們分別在100個動態測試模塊的攔截器上加入了``AMockApi``: ```typescript providers: [ { provide: HTTP_INTERCEPTORS, multi: true, useClass: MockApiInterceptor.forRoot([AMockApi]) } ] ``` 然后正常的事情發生了:由于需求的變更,A組件增加了新功能,新功能添加完成后,不但要依賴于`AMockApi`,還依賴于`BMockApi`。 想像一下當A組件的功能功能后,使用`ng t`來測試其它100個組件時,每個組件都需要加入到`BMockApi`的依賴: ```typescript providers: [ { provide: HTTP_INTERCEPTORS, multi: true, useClass: MockApiInterceptor.forRoot([AMockApi, BMockApi]) } ] ``` 在Angular中,可以建立一個專門用于模擬后臺的模塊,在該模塊中加入所有的`MockApi`來模擬整個后臺。然后再其它需要使用模擬后臺的測試模塊中,僅僅引入這個專門的模塊即可。以前測試模塊為例,按照傳統的解決辦法,若想成功創建教師編輯組件,則應該在`imports`中加入`HttpClientModule`: ```typescript beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [EditComponent, KlassSelectComponent], imports: [ HttpClientModule ] }) .compileComponents(); }); ``` 原理圖對應如下: ![image-20210401154227192](https://img.kancloud.cn/e0/ce/e0cea73195649747bcc6800ebf4a8b87_1690x546.png) 而如果想使用`MockApi`來替代真實的后臺Api,則需要在`provider`中提供`HTTP_INTERCEPTORS`: ```typescript await TestBed.configureTestingModule({ declarations: [EditComponent, KlassSelectComponent], imports: [ HttpClientModule ], providers: [ { provide: HTTP_INTERCEPTORS, multi: true, useClass: MockApiInterceptor.forRoot([]) } ] }) .compileComponents(); ``` 此時`MockApi`請作用的原因是由于`HttpClient`在發起數據請求以前,會查看當前模塊中提供的`HTTP_INTERCEPTORS`,如果當前模塊提供了`HTTP_INTERCEPTORS`則會調用第一個`HTTP_INTERCEPTOR`,然后由第一個調用第二個,依次累推。 ![image-20210401155231459](https://img.kancloud.cn/3c/3b/3c3baf656b03d0fd4320453fb7b77ff8_1212x612.png) 但`MockApi`不有按常規出牌,當調用到它時,它沒有繼續調用下一個攔截器,而是直接自己用模擬數據回應了: ![image-20210401155426811](https://img.kancloud.cn/7b/b1/7bb107384234a13913ab95e64d929abf_1126x594.png) 這就是在加入`MockApiInterceptor.forRoot()`攔截器后模擬API起作用的原因。 而`MockApiInterceptor`是根據`forRoot()`方法中傳入的數組來返回對應的模擬數據的,所以若想某些模擬數據生效,是需要加相應的類加入到`forRoot()`方法中: ```typescript beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [EditComponent, KlassSelectComponent], imports: [ HttpClientModule ], providers: [ { provide: HTTP_INTERCEPTORS, multi: true, useClass: MockApiInterceptor.forRoot([TeacherMockApi]) } ] }) .compileComponents(); ``` 以上我們再一次解釋了引用`HttpModule`以及`MockApi`的原理。 ## 公共測試模塊 我們添加一個公共測試模塊的話,原理圖如下: ![image-20210401160603834](https://img.kancloud.cn/c7/6c/c76c2dffd822b5f4e738ed241cb581e1_1672x1084.png) 我們將提供`HttpClient`的`HttpModule`以及提供攔截器的`MockApiInterceptor`添加到新的模塊中,然后在動態測試模塊中引用這個新模塊。這樣以來,將動態測試模塊需要`httpClient`時,則會由其`imports`的**專用用于模擬全部后臺Api的模塊**中的`HttpModule`中引入;當`httpClient`發起請求時,也會由**專用用于模擬全部后臺Api的模塊**中查找`HTTP_INTERCEPTORS`。 下面,讓我們共同來實現上述原理圖。 ### 建立模塊 該模塊的作用是為單元測試中的動態測試模塊提供MockApi功能,將其命名為`MockApiTestingModule`,并將其建立在`mock-api`下是個不錯的選擇: ```bash panjie@panjie-de-Mac-Pro mock-api % pwd /Users/panjie/github/mengyunzhi/angular11-guild/first-app/src/app/mock-api panjie@panjie-de-Mac-Pro mock-api % ng g m MockApiTesting --flat CREATE src/app/mock-api/mock-api-testing.module.ts (200 bytes) ``` **注意:** 上述命令使用了`--flat` 參數,表示把文件創建到當前文件夾,如果不加該參數,`ng`將會當前文件夾下創建一個`mock-api-testing`文件夾。 此時`mock-api`文件夾下共存在3個文件: ```bash CREATE src/app/mock-api/mock-api-testing.module.ts (200 bytes) panjie@panjie-de-Mac-Pro mock-api % tree . ├── clazz.mock.api.ts ├── mock-api-testing.module.ts └── teacher.mock.api.ts 0 directories, 3 files ``` 然后打開`mock-api-testing.module.ts`,為其添加對`HttpModule`的依賴,以及提供MockApi攔截器: ```typescript +++ b/first-app/src/app/mock-api/mock-api-testing.module.ts @@ -1,11 +1,20 @@ import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; +import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http'; +import {MockApiTestingInterceptor} from '@yunzhi/ng-mock-api/testing'; @NgModule({ declarations: [], imports: [ + HttpClientModule, CommonModule + ], + providers: [ + { + provide: HTTP_INTERCEPTORS, multi: true, + useClass: MockApiTestingInterceptor.forRoot([]) + } ] }) export class MockApiTestingModule { ``` 接下來將所有的MockApi文件,添加到`forRoot([])`方法中的數組中: ```typescript - useClass: MockApiTestingInterceptor.forRoot([]) + useClass: MockApiTestingInterceptor.forRoot([ + ClazzMockApi, + TeacherMockApi + ]) ``` ## 應用模塊 現在可以在相關的測試中引用`MockApiTestingModule`來快速的滿足組件的各種后臺請求了,打開班級編輯組件對應的測試文件,如此配置動態測試模塊: ```typescript +++ b/first-app/src/app/clazz/edit/edit.component.spec.ts import {MockApiTestingModule} from '../../mock-api/mock-api-testing.module'; beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [EditComponent, KlassSelectComponent], imports: [ MockApiTestingModule ] }) .compileComponents(); }); ``` 此時再查看`ng t`,在控制臺出現了找不到`formControl`的異常。 ![image-20210402092204041](https://img.kancloud.cn/7f/bc/7fbcf4e29250f4d28e0100a27ef45554_1206x92.png) 這是由于教師選擇組件中使用了響應式表單,而當前動態測試模塊并沒有引入響應式表單模塊,所以提示無法識別`formControl`的錯誤。解決的方法是在當前動態測試模塊中引用響應式表單模塊: ```typescript - MockApiTestingModule + MockApiTestingModule, + ReactiveFormsModule ``` 此時控制臺錯誤消息。錯誤雖然消失了,但當前方案仍存在一定的問題。這是由于我們在解決該問題時,仍然需要考慮子組件對`ReactiveFormsModule`的依賴問題,如果子組件依賴于多個模塊,我們同樣還需要在當前的單元測試中引入其它多個模塊。所以從本質上來,我們仍然面臨著某個子組件進行更新后,引用它的父組件需要全部在`imports`中增加一遍對應模塊的問題。帶著這個問題繼續學習,我們將在后面的章節中同樣使用抽離模塊的方法來解決該問題。 最后,由于在`MockApiTestingModule`中使用的攔截器是`MockApiTestingInterceptor`而非`MockApiInterceptor`,所以我們需要在測試代碼中手動觸發數據返回,同時啟用變更檢測。 ```typescript fit('should create', () => { expect(component).toBeTruthy(); + getTestScheduler().flush(); + fixture.autoDetectChanges(); }); ``` 最終效果如下: ![image-20210402093456159](https://img.kancloud.cn/48/cd/48cdff073cc4185dc865b053300ea27e_1116x414.png) 至此,基于教師選擇組件的班級編輯組件原型初始化完畢。 | 名稱 | 鏈接 | | ---------- | ------------------------------------------------------------ | | 定義提供者 | [https://angular.cn/guide/dependency-injection-providers#defining-providers](https://angular.cn/guide/dependency-injection-providers#defining-providers) | | 本節源碼 | [https://github.com/mengyunzhi/angular11-guild/archive/step6.4.1.zip](https://github.com/mengyunzhi/angular11-guild/archive/step6.4.1.zip) |
                  <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>

                              哎呀哎呀视频在线观看