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

                前后臺的基本完成后,進行集成測試來驗證其對接情況。按老的步驟:啟動數據庫 -> 啟動后臺 -> 啟動前臺 -> 添加用于支持測評編輯學生功能的基礎數據 -> 添加路由以使得可以通過URL或跳轉的方式來查看編輯頁面 -> 按控制臺報錯提示添加編輯組件的依賴 -> 進行測試并按完善代碼或修正BUG。 啟動數據庫的步驟略過。 # 啟動后臺 方法一:直接在idea中啟動 ![](https://img.kancloud.cn/62/82/62822a715cfcc1ce455e6b63057068c9_523x69.png) 方法二:使用命令行啟動 ![](https://img.kancloud.cn/b4/a6/b4a6b536dcd401602d9585199f189f11_781x117.png) # 啟動前臺 使用webstorm打開前臺項目,并使用`ng serve`來啟動項目: ![](https://img.kancloud.cn/f6/ba/f6baacc170487b415db153199addf411_1080x165.png) # 添加基礎數據 打開[http://localhost:4200/](http://localhost:4200/)。在教師管理中添加一個教師,在班級管理中添加不少于2個班級,在學生管理中添加不小于2個學生,各個學生應該有所區別。 ![](https://img.kancloud.cn/cf/29/cf2967fc1885095baa4653d523a61f50_1078x228.png) ![](https://img.kancloud.cn/a6/cf/a6cf12ef0b09ed8a8ea1efd2c236fd9b_1048x178.png) # 添加路由 如果你和我一樣早就忘記了如何添加路中,則可以打開前面已經開發過的`src/app/app-routing.module.ts`參考。然后打開學生模塊對應的路由配置文件:`src/app/student/student-routing.module.ts` ``` import {EditComponent} from './edit/edit.component'; ? const routes: Routes = [ { path: 'add', component: AddComponent }, { path: '', component: IndexComponent }, ? { ? path: 'edit/:id', ? component: EditComponent ? } ? ]; ``` * ? 注意這里應該多加一個`,`。在數組中,各個項用`,`進行分離。 # 添加依賴 打開chrome的控制臺。然后按剛剛添加的路由信息,在地址欄中對應的輸入相應地址。繼而查看控制臺報錯信息,最后按報錯提示添加對應依賴。 ![](https://img.kancloud.cn/cc/21/cc2145e783204c90116fa32d32999c5e_901x246.png) 控制臺信息: ![](https://img.kancloud.cn/f2/e0/f2e03879231d15d834ad75248849c98e_591x179.png) 控制臺無報錯,看來當前StudentModule有提供編輯組件中所需全部依賴的能力。 # 測試并修正 要想更好的測試編輯功能,就需要在學生列表中加入相應的鏈接,使用點擊鏈接的方式進行測試。 ## 加入鏈接 src/app/student/index/index.component.html ``` <td>{{student.klass.name}}</td> <td><a routerLink="./edit/{{student.id}}">編輯</a> </td> ? </tr> </table> ``` ## 點擊測試 ![](https://img.kancloud.cn/eb/ad/ebadd2c3472e91a4efccc81882d31e0d_1125x312.gif) 為增強些視覺的交互效果,為`編輯`加幾個樣式,使它看起來更順眼些: ``` <td><a routerLink="./edit/{{student.id}}" class="btn btn-sm btn-info">編輯</a> </td> ``` ![](https://img.kancloud.cn/52/26/5226d52926642b0fdd73bfe803c1df22_1086x59.png) ## 測試問題一 編輯時班級未選中的問題: ![](https://img.kancloud.cn/fb/95/fb95cc072c035bf2fc739d7ceae3bee3_777x89.png) 解決該問題相對較復雜,將在下一小節中單元進行講解。 ## 測試問題二 選擇班級后報錯: ![](https://img.kancloud.cn/72/43/7243015162425f3c2b26e6c7844ad371_848x264.png) 原則上這個問題應該在單元測試時被測試出來,但單元測試中由于當前測試思路(在思想上,總相按原來的方法獲取這個班級選擇組件的替身,但又沒有找到獲取該組件的方法)尚無法進行嵌入式組件的輸入、輸出測試,所以單元測試跳過了該部分。 按錯誤提示:在學生編輯組件的V層的第5行: src/app/student/edit/edit.component.html ``` <label>班級:<app-klass-select [klass]="student.klass" (selected)="onSelectKlass($event)"></app-klass-select></label> ``` 發生錯誤:onSelectKlass不是一個函數。 原來在V層聲明了要調用C層的onSelectKlass,但卻忘記在C層中寫相應的方法了,補充如下: src/app/student/edit/edit.component.ts ``` /** * 選擇班級 * @param $event 班級 */ onSelectKlass($event: Klass) { this.student.klass = $event; } ``` 再次測試錯語消失。 ## 測試問題三 點擊"保存"按鈕未觸發網絡請求。 ![](https://img.kancloud.cn/ec/ff/ecffacb58c7751b9b9a201171c04caef_1137x391.gif) 但我們很確信在單元測試中測試了這個功能。找到對應的單元測試代碼來排查問題: src/app/student/edit/edit.component.spec.ts ``` it('點擊保存按鈕', () => { spyOn(component, 'onSubmit'); const button: HTMLButtonElement = fixture.debugElement.query(By.css('button')).nativeElement; button.click(); expect(component.onSubmit).toHaveBeenCalled(); }); it('onSubmit', () => { // 設置formGroup的值 const name = Math.random().toString(36).slice(-10); const sno = Math.random().toString(36).slice(-10); component.formGroup.get('name').setValue(name); component.formGroup.get('sno').setValue(sno); // 設置班級選擇組件的值 // todo: 在官方文檔中暫未找到相應的示例代碼 // 調用onSubmit方法 component.onSubmit(); // 斷言已使用formGroup及班級選擇組件的值更新了學生信息 expect(component.student.name).toBe(name); expect(component.student.sno).toBe(sno); // expect(component.student.klass).toBe(null); todo: 原則上這里要測試發射值 // 斷言調用 向M層傳入更新的學生ID及更新的學生信息 方法 ★ }); ``` * ★ 貌似找到問題了,在單元測試只寫了注釋卻沒有完成相應的代碼。 為此將此測試用例的`it`暫時修改為`fit`,啟用`ng test`后修正代碼如下: src/app/student/edit/edit.component.spec.ts ``` fit('onSubmit', () => { // 設置formGroup的值 const name = Math.random().toString(36).slice(-10); const sno = Math.random().toString(36).slice(-10); component.formGroup.get('name').setValue(name); component.formGroup.get('sno').setValue(sno); // 設置班級選擇組件的值 // todo: 在官方文檔中暫未找到相應的示例代碼 // 設置update方法替身 spyOn(component, 'update'); ? // 調用onSubmit方法 component.onSubmit(); // 斷言已使用formGroup及班級選擇組件的值更新了學生信息 expect(component.student.name).toBe(name); expect(component.student.sno).toBe(sno); // expect(component.student.klass).toBe(null); todo: 原則上這里要測試發射值 // 斷言調用 向M層傳入更新的學生ID及更新的學生信息 方法 expect(component.update).toHaveBeenCalledWith(component.student); ? }); ``` ![](https://img.kancloud.cn/6b/9d/6b9d1867c135dc7abd9d7b1e022691e9_2348x130.png) 報錯:期望調用 componet.update方法,但根本就沒調用過。 補充C層代碼如下: src/app/student/edit/edit.component.ts ``` onSubmit() { this.student.name = this.formGroup.get('name').value; this.student.sno = this.formGroup.get('sno').value; this.student.klass = this.student.klass; this.update(this.student); ? } ``` 測試順序通過。此時正常發起了網絡請求且成功更新了數據。 ![](https://img.kancloud.cn/75/74/75746db417019fb393fd1690e960401a_1141x370.gif) ## 測試問題四 點擊保存按鈕后未自動跳轉至index。 實現跳轉的方法有兩個:第一種是直接在C層中寫跳轉的代碼;第二種是在V層中增加一個"隱藏"的跳轉鏈接,然后在C層中進行模擬點擊,進行達到跳轉的效果。由于第二種方法支持相對路徑,可以適應變化的路由,在此仍然采用第二種方案。具體可參考學生模塊的添加組件: src/app/student/edit/edit.component.ts ``` /** * 更新學生 * @param student 學生 */ update(student: Student) { this.studentService.update(student.id, student) .subscribe((result) => { this.student = result; this.linkToIndex.nativeElement.click(); ? }); } ``` 但在測試時點擊保存按鈕后卻在控制臺打印了如下錯誤信息: ![](https://img.kancloud.cn/1c/b2/1cb2c12a584ad4eba75a944d7664ae73_892x121.png) 它在說:當點擊保存按鈕時,嘗試跳轉到`student/edit`路由,但該路由未能匹配任何路由規則,所以當前路由無法進行跳轉。 分析這個問題還要從兩方面入手: 1. 當前路徑 2. 相對路徑的設置 當前路徑為:`http://localhost:4200/student/edit/1`, V層中相對路徑的設置為:`<a style="display: none" routerLink="./../" #linkToIndex>返回學生列表頁</a>`中的`./../`。其中`./`代表當前路徑即:`http://localhost:4200/student/edit/1` ;`./../`則代表當前路徑的上級路徑,則對應的路徑為:`http://localhost:4200/student/edit`。而這個路徑的確未對應任何的路由,所以得到了上面的錯誤。而實際上此處跳轉的目標是:`http://localhost:4200/student`,所以此時的的相對路徑應該修正為: ``` routerLink="./../../" ``` 修正后測試正常。 > 請思索:為何在添加組件中寫`./../`而不是`./../../`呢? 至此,除編輯時未選中當前klass以外,其它的編輯功能正常。集成測試完成。 ## 驗證測試 很長時間沒有驗證其它單元測試是否還在正確的運行了。下面請把單元測試中所有的`fdescribe`變更為`describe`,所有的`fit`變更為`it`。跑一遍單元測試來查看是否由于添加新的功能而對歷史功能造成了影響。 ![](https://img.kancloud.cn/62/27/622788b7f5dedb32825f57bf2b3084f1_644x158.png) 測試結果:60的測試用例,失敗了28個。看到這樣的數字后不必進行驚慌,由于單元測試是一點點的補充過來的,所以不應該出現大量的單元測試報錯的情況。如果有,那么出現錯誤的原因也必然集中到一個點上。把一個解決了,其它的也就是隨著解決了。 ``` Student -> IndexComponent > 測試頁碼號 Failed: Template parse errors: Can't bind to 'routerLink' since it isn't a known property of 'a'. (" ``` 錯誤信息:不認識`routerLink`。`routeLink`由`路由模塊`提供。此錯誤的原因應該在測試模塊中未引入相應的路由模塊。 src/app/student/index/index.component.spec.ts ``` beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [IndexComponent, KlassSelectComponent], imports: [ ReactiveFormsModule, FormsModule, CoreModule, HttpClientTestingModule, RouterTestingModule ? ], providers: [ {provide: StudentService, useClass: StudentStubService} ] }) .compileComponents(); })); ``` 果然加入此信息后,失敗的單元測試用例由28個銳減到了2個。 ![](https://img.kancloud.cn/e6/3f/e63f5dd6fab861bf77115e4f286a7a86_890x130.png) 繼續修正: ``` Student -> IndexComponent > 當前頁、總頁數、每頁大小 Expected ' 第1/100頁 每頁2條 ' to contain '第0/100頁'. Error: Expected ' 第1/100頁 每頁2條 ' to contain '第0/100頁'. ``` 看到此測試突然想起來了:當初修正過分頁的當前頁碼(由0變成1),也修改過頁碼的提示,但卻未對應修正單元測試的內容。看來以后再進行功能修正的時候,一定要同步修正單元測試。否則時間一長,可能就判斷不出是單元測試用例寫的有問題還是功能性代碼寫的有問題了。 src/app/student/index/index.component.spec.ts ``` it('當前頁、總頁數、每頁大小', () => { /* 獲取分頁信息 */ const debugElement = fixture.debugElement.query(By.css('#pageInfo')); const pageInfoDiv: HTMLDivElement = debugElement.nativeElement; const text = pageInfoDiv.textContent; console.log(text); /* 斷言綁定了C層的分頁值 */ expect(text).toContain(`第${component.params.page}/${component.pageStudent.totalPages}頁`); ? expect(text).toContain(`第${component.params.page + 1}/${component.pageStudent.totalPages}頁`); ? expect(text).toContain(`每頁${component.params.size}條`); ? expect(text).toContain(`第${component.params.page + 1}/${component.pageStudent.totalPages}頁 每頁${component.params.size}條`); ? }); ``` 繼續修正: ``` Expected '編輯' to be ''. Error: Expected '編輯' to be ''. at <Jasmine> at UserContext.<anonymous> (http://localhost:9876/_karma_webpack_/src/app/student/index/index.component.spec.ts:73:62) ``` 提示說:`src/app/student/index/index.component.spec.ts`第73行62列出現錯誤:期望是空字符串,但實現卻是編輯。 這是由于當前在index中增加了編輯按鈕造成的。找到對應的測試代碼,修正為: src/app/student/index/index.component.spec.ts ``` expect(table.rows.item(row).cells.item(col++).innerText).toBe('testKlass'); expect(table.rows.item(row).cells.item(col++).innerText).toBe(''); ? expect(table.rows.item(row).cells.item(col++).innerText).toBe('編輯'); ? }); ``` 最終單元測試全部通過: ![](https://img.kancloud.cn/49/5e/495e06db45e220ff9ee52aa58c2fb948_445x75.png) # 寫在最后 在后期的修正或是遞增開發時,往往會忽略掉集成測試,或是在集成測試中僅測試部分功能。因為相對于可以被自動執行的單元測試,集成測試的確是太耗時且充滿著不確定性了。做集成測試需要對整個模塊或是整個功能進行全方位的了解。比如此處的集成測試就需要先準備好教師數據,再準備好班級數據,最后還要準備好學生數據。現在數據庫中才僅僅有3個數據表便需如此,數據表稍微一多便無法想像。這也是教程伊始便帶領大家走向這難學、難懂、抽象、難掌握的單元測試的原因。在單元測試中,可以大膽的忽略到這些問題,可以不考慮其它的組件的工作方法,把精力集中到單一的組件開發中來;不僅如此,一理建立的正確的單元測試代碼,但可以在任何時候來保障功能的正確性,從長期來看這的確是一筆相當劃算的買賣。 如果你對此已小有感觸,那么是否考慮到要把點擊"保存"扭鈕后正常跳轉到列表頁寫入單元測試呢?如果沒有單元測試的保障,假設有一天將編輯的路由path由`edit/:id`變更為`edit/:id/:username `時,是否能夠快速的發現跳轉功能已失效的錯誤呢?請繼續閱讀后續章節。 # 參考文檔 | 名稱 | 鏈接 | 預計學習時長(分) | | --- | --- | --- | | 源碼地址 | [https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step4.7.6](https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step4.7.6) | - |
                  <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>

                              哎呀哎呀视频在线观看