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

                本節繼續前一小節的單元測試,完成提交數據的單元測試部分。 # 初始化 在編寫代碼前先寫清自己編寫代碼的功能以及實現功能的步驟是個非常好的開發習慣,這將使你在處理復雜的問題的時候顯得得心應手: klass/edit/edit.component.spec.ts ``` /** * 數據更新測試,步驟: * 1. 設置路由參數 * 2. 輸入input的值 * 3. 點擊提交扭鈕:斷言向預期的地址以對應的方法提交了表單中的數據 * 4. 斷言跳轉到''路由地址 */ fit('更新數據測試', () => { expect(component).toBeTruthy(); }); ``` ## 造輪子 測試方法需要設置input的值以值點擊對應的提交按鈕,由于此方法前面我們使用過,所以確定為重復的方法,故在testing/FormTest.ts中新造輪子如下: ``` /** * 設置input的值 * @param fixture 夾具 * @param cssSelector CSS選擇器 * @param value 要設置的值 * @return 成功true 失敗false */ static setInputValue(fixture: ComponentFixture<any>, cssSelector: string, value: string): boolean { const selectorElement = this.getSelectorElement(fixture, cssSelector); if (isNull(selectorElement)) { return false; } const htmlInputElement: HTMLInputElement = selectorElement.nativeElement; htmlInputElement.value = value; htmlInputElement.dispatchEvent(new Event('input')); return true; } /** * 獲取button按鈕,并點擊 * @param fixture 夾具 * @param cssSelector CSS選擇器 * @return 成功true 失敗false */ static clickButton(fixture: ComponentFixture<any>, cssSelector: string): boolean { const selectorElement = this.getSelectorElement(fixture, cssSelector); if (isNull(selectorElement)) { return false; } const htmlButtonElement: HTMLButtonElement = selectorElement.nativeElement; htmlButtonElement.click(); return true; } /** * 根據CSS選擇器來獲取夾具中Debug元素 * @param fixture 夾具 * @param cssSelector CSS選擇器 * @return Debug元素 */ static getSelectorElement(fixture: ComponentFixture<any>, cssSelector: string): DebugElement { const debugElement: DebugElement = fixture.debugElement; return debugElement.query(By.css(cssSelector)); } ``` 在造上述輪子的時候我們發現兩個輪子中也存在同樣的方法,于是在輪子中又剝離中一個新輪子`getSelectorElement`。 ## 完善測試 我們添加一個onSubmitTest方法用于測試更新操作,由于更新操作依賴于數據獲取操作,所以該方法在數據成功獲取后調用: ``` const testGetHttp = (id: number) => { ... fixture.whenStable().then(() => { ... onSubmitTest(1); ① }); }; /** * 數據更新測試,步驟: * 1. 設置路由參數 * 2. 輸入input的值 * 3. 點擊提交扭鈕:斷言向預期的地址以對應的方法提交了表單中的數據 * 4. 斷言跳轉到''路由地址 */ const onSubmitTest = (id: number) => { FormTest.setInputValue(fixture, '#name', '測試更新班級'); FormTest.setInputValue(fixture, '#teacherId', '100'); fixture.whenStable().then(() => { FormTest.clickButton(fixture, 'button'); const httpTestController: HttpTestingController = TestBed.get(HttpTestingController); const req = httpTestController.expectOne(`http://localhost:8080/Klass/${id}`); expect(req.request.method).toEqual('PUT'); const klass: Klass = req.request.body.valueOf(); expect(klass.name).toEqual('測試更新班級'); expect(klass.teacher.id).toEqual(100); req.flush(null, {status: 204, statusText: 'No Content'}); httpTestController.verify(); ? }); }; ``` * ① 獲取要編輯的數據后,開始測試編輯數據 * ? httpTestController.verify(); 來保證所有的http請求都是符合我們的預期的。在執行此方法時如果還有其它的http請求,則會在控制臺中打印異常信息。 由于一些異常僅僅會打印在控制臺中而未反饋到測試界面上,所以我們在測試的過程中應該:**輕界面重控制臺** # 測試間諜SPY 我們看似完成了所有的操作,但來到klass/edit/edit.component.ts中卻發現我們并不能夠確認這行代碼是否成功的被執行了: klass/edit/edit.component.ts ``` /** * 用戶提交時執行的操作 */ onSubmit(): void { const data = { name: this.formGroup.value.name, teacher: {id: this.formGroup.value.teacherId} }; this.httpClient.put(this.getUrl(), data) .subscribe(() => { this.router.navigateByUrl('', {relativeTo: this.route}); ① }, () => { console.error(`在${this.getUrl()}上的PUT請求發生錯誤`); }); } ``` * ① 此行代碼是否成功的觸發我們并不清楚 要測試該方法是否成功的被執行了,我們使用上個小節剛剛講過的方法。即:模擬一個和this.route一樣具有`navigateByUrl`方法的服務,然后在`providers`中聲明使用該模擬服務來替換原服務,最終我們去測試模擬服務中該方法是否被觸發了。比如: router-stub.ts ``` import {ActivatedRoute} from '@angular/router'; export class RouterStub { path: string; config: {relativeTo: ActivatedRoute}; navigateByUrl(path: string, config: {relativeTo: ActivatedRoute}): void { this.path = path; this.config = config; } } ``` 然后我們使用`providers`來注入此`RouterStud`來替換原來的`Router`。最終我們來斷言`RouterStud`中的`path`是否為`''`。為`''`則說明執行了此方法;仍然是`undefined`則說明未執行此方法。 klass/edit/edit.component.ts ``` providers: [ {provide: ActivatedRoute, useClass: ActivatedRouteStub}, {provide: Router, useClass: RouterStub} ] ... req.flush(null, {status: 204, statusText: 'No Content'}); httpTestController.verify(); const router: RouterStub = TestBed.get(Router); ? expect(router.path).toEqual(''); ? }); ``` 除此以外angular來為我們提供了更為簡單的測試方法 ---- 測試間諜 ## 生成間諜 間諜的作用同我們剛剛建立的`stub`模擬服務一樣,都是向測試的組件提供一個覆蓋原服務的測試服務。但測試間諜的生成方法更加的簡單,比如我們在使用`spy`方法來生成一個Router測試服務,則只需要:`const routerSpy = jasmine.createSpyObj<Router>('Router', ['navigateByUrl']);`即可。 它表示:創建一個擁有`navigateByUrl`方法的模擬`Router`的間諜服務。有了這個服務,我們直接將其添加到`providers`中即可: klass/edit/edit.component.ts ``` beforeEach(async(() => { const routerSpy = jasmine.createSpyObj<Router?>('Router', ['navigateByUrl']); TestBed.configureTestingModule({ declarations: [EditComponent], imports: [ ReactiveFormsModule, HttpClientTestingModule, RouterTestingModule ], providers: [ {provide: ActivatedRoute, useClass: ActivatedRouteStub}, {provide: Router, useValue?: routerSpy} ] }) .compileComponents(); })); ``` * ? 在使用RouterStub時,我們使用關鍵字`useClass`來表示:當組件需要具有Router功能的對象(服務)時,使用RouterStub`類`來實例化一個對象給它。 * ? 在使用routerSpy時,我們我們使用關鍵字`useValue`來表示 :當組件需要具有Router功能的對象(服務)時,將routerSpy的`值`給它。 * ? 使用`<Router>`表示該間諜的根據`Router`生成的并用于模擬`Router`功能的。 ## 使用間諜斷言 klass/edit/edit.component.ts ``` /** * 數據更新測試,步驟: * 1. 設置路由參數 * 2. 輸入input的值 * 3. 點擊提交扭鈕:斷言向預期的地址以對應的方法提交了表單中的數據 * 4. 斷言跳轉到''路由地址 */ const onSubmitTest = (id: number) => { FormTest.setInputValue(fixture, '#name', '測試更新班級'); FormTest.setInputValue(fixture, '#teacherId', '100'); fixture.whenStable().then(() => { FormTest.clickButton(fixture, 'button'); const httpTestController: HttpTestingController = TestBed.get(HttpTestingController); const req = httpTestController.expectOne(`http://localhost:8080/Klass/${id}`); expect(req.request.method).toEqual('PUT'); const klass: Klass = req.request.body.valueOf(); expect(klass.name).toEqual('測試更新班級'); expect(klass.teacher.id).toEqual(100); const routerSpy: SpyObj<Router> = TestBed.get(Router); ? expect(routerSpy.navigateByUrl.calls.any()).toBe(false); ? req.flush(null, {status: 204, statusText: 'No Content'}); expect(routerSpy.navigateByUrl.calls.any()).toBe(true); ? httpTestController.verify(); }); }; ``` * ? 獲取我們在providers中聲明的spy對象。 * ? 斷言:該間諜服務的navigateByUrl從未調用過。 * ? 斷言:該間諜服務的navigateByUrl已經被調用過了。 至此,我們在間諜的幫助下快速的完成了編輯組件提交的測試工作。 # 參考文檔 | 名稱 | 鏈接 | 預計學習時長(分) | | --- | --- | --- | | 源碼地址 | [https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step3.4.4](https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step3.4.4) | - | | 使用間諜(spy)進行測試 | [https://www.angular.cn/guide/testing#testing-with-a-spy](https://www.angular.cn/guide/testing#testing-with-a-spy) | | Spy | [https://jasmine.github.io/api/3.3/Spy.html](https://jasmine.github.io/api/3.3/Spy.html) | | | Spy calls | [https://jasmine.github.io/api/3.3/Spy\_calls.html](https://jasmine.github.io/api/3.3/Spy_calls.html) | | 依賴提供商 | [https://www.angular.cn/guide/dependency-injection-providers#dependency-providers](https://www.angular.cn/guide/dependency-injection-providers#dependency-providers) | 15 |
                  <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>

                              哎呀哎呀视频在线观看