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

                本節完成用戶登錄組件的功能設計,與前面開發的其它組件相同:在C層中完成與V層的交互工作,在M層中完成與后臺的對接部分,并借助單元測試最終完成開發。 # CV層交互 V層中有兩項交互的內容:表單信息及提交按鈕。 ## 表單信息 表單信息的交互采用更加面向對象的formGroup,代碼如下: src/app/login/login.component.ts ```javascript formGroup: FormGroup; constructor() { } ngOnInit() { this.formGroup = new FormGroup({ username: new FormControl(''), password: new FormControl('') }); } ``` src/app/login/login.component.html ```html <form [formGroup]="formGroup"?> <input type="text" class="form-control" id="username" aria-describedby="usernameHelp" placeholder="用戶名" formControlName="username"?> <input type="password" class="form-control" id="password" placeholder="密碼" formControlName="password"?> ``` ### 測試 src/app/login/login.component.spec.ts ```javascript fit('表單綁定', () => { // 設置C層的值后重新渲染V層 component.formGroup.get('username').setValue('testUsername'); component.formGroup.get('password').setValue('testPassword'); fixture.detectChanges(); // 獲取V層的值 const usernameValue = FormTest.getInputValueByFixtureAndCss(fixture, '#username'); const passwordValue = FormTest.getInputValueByFixtureAndCss(fixture, '#password'); // 斷言CV兩層的值相等 expect(usernameValue).toEqual('testUsername'); expect(passwordValue).toEqual('testPassword'); }); ``` 測試結果: ``` Failed: Template parse errors: Can't bind to 'formGroup' since it isn't a known property of 'form'. ("<div class="row justify-content-center"> <div class="col-4"> <form [ERROR ->][formGroup]="formGroup"> <div class="form-group"> <label for="username">用戶名</label> "): ng:///DynamicTestModule/LoginComponent.html@2:10 ``` 提示說:不認識formGroup,這是由于formFroup存在于ReactiveFormsModule中,修正如下: src/app/login/login.component.spec.ts ```javascript beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ LoginComponent ], imports: [ ? ReactiveFormsModule ? ] ? }) .compileComponents(); })); ``` 再次運行測試通過,且用戶名密碼被填充至相應的input中: ![](https://img.kancloud.cn/4f/6a/4f6a5d88cc5a571b005a117d1f935d86_446x250.png) ## 提交按鈕 點擊提交按鈕時,應該觸發C層相應的方法,設定方法名為:onSubmit。 src/app/login/login.component.html ```html <form [formGroup]="formGroup" (ngSubmit)="onSubmit()"> ``` src/app/login/login.component.ts ```javascript onSubmit() { } ``` ### 單元測試 src/app/login/login.component.spec.ts ```javascript fit('點擊提交按鈕', () => { spyOn(component, 'onSubmit'); FormTest.clickButton(fixture, 'button'); expect(component.onSubmit).toHaveBeenCalled(); }); ``` 組件的CV層對接測試完畢后,開始進行CM層的對接測試。 # CM層對接 由于M層最終需要對后臺的數據進行請求,所以到了制定前臺后對接規范的時候了。用戶提交用戶名密碼時,如果用戶名密碼正確,則返回true;如果用戶名密碼不正確,則返回false。定制接口規范如下: ``` POST /Teacher/login ``` #### 參數 Parameters | type | name | Description | Schema | | --- | --- | --- | --- | | **Body** | **用戶名密碼** <br> *requried* | 登錄教師 | {username: 用戶名, password: 密碼} | #### 返回值 Responses | HTTP Code | Description | Schema | | --- | --- | --- | | **200** | Ok | 用戶密碼是否正確:正確,true; 不正確, false | 定制相應的時序圖如下: ![](https://img.kancloud.cn/b1/a7/b1a73eac38cffb5b79f084c6ed11cefa_377x101.png) ## M層開發 按由后向前的順序,先進行M層的開發。來到src/app/service文件夾,新建service及對應的單元測試文件: ``` panjiedeMac-Pro:service panjie$ ng g s teacher CREATE src/app/service/teacher.service.spec.ts (338 bytes) CREATE src/app/service/teacher.service.ts (136 bytes) ``` 添加login方法: src/app/service/teacher.service.ts ```javascript import { Injectable } from '@angular/core'; import {Observable} from 'rxjs'; @Injectable({ providedIn: 'root' }) export class TeacherService { constructor() { } /** * 用戶登錄 * @param username 用戶名 * @param password 密碼 * @return 登錄成功:true; 登錄失敗: false。 */ login(username: string, password: string): Observable<boolean> { return null; } } ``` 完善功能 src/app/service/teacher.service.ts ```javascript export class TeacherService { constructor(private httpClient: HttpClient) { } /** * 用戶登錄 * @param username 用戶名 * @param password 密碼 * @return 登錄成功:true; 登錄失敗: false。 */ login(username: string, password: string): Observable<boolean> { const url = 'http://localhost:8080/Teacher/login'; return this.httpClient.post<boolean>({username, password}); } } ``` ### 單元測試 此類的單元測試已經做過很多遍了: src/app/service/teacher.service.spec.ts ```javascript beforeEach(() => TestBed.configureTestingModule({ imports: [ HttpClientTestingModule ] })); ... fit('login', () => { // 獲取service實例 const service: TeacherService = TestBed.get(TeacherService); // 準備接收值,調用login方法并訂閱以使其發起請求 let result: boolean; service.login('username', 'password').subscribe(value => { result = value; }); // 獲取請求信息,并斷言請求地址、方法、請求的值符合預期 const httpTestingController: HttpTestingController = TestBed.get(HttpTestingController); const req = httpTestingController.expectOne('http://localhost:8080/Teacher/login'); expect(req.request.method).toEqual('POST'); const usernameAndPassword: { username: string, password: string } = req.request.body.valueOf(); ? expect(usernameAndPassword.username).toEqual('username'); expect(usernameAndPassword.password).toEqual('password'); // 模擬返回請求值,斷言在訂閱中接收到了該值 req.flush('true'); ? expect(result).toBeTruthy(); }); ``` * ? 使用valueOf()時,應該規定獲取后的變量類型。比如此處為: `{ username: string, password: string }` * ? 此處應該使用`req.flush('true');`,而非`req.flush(true);` ## C層開發 C層的功能可以描述為:獲取用戶輸入的用戶名、密碼信息,然后將用戶名密碼信息發送到M層,對應代碼如下: src/app/login/login.component.ts ```javascript /** * 點擊提交按鈕后進行用戶登錄 */ onSubmit() { const username = this.formGroup.get('username').value; const password = this.formGroup.get('password').value; this.teacherService.login(username, password).subscribe(result => { console.log(result); }); } ``` ### 單元測試 src/app/login/login.component.spec.ts ```javascript imports: [ ReactiveFormsModule, HttpClientTestingModule ? ] fit('onSubmit', () => { // 獲取teacherService實例,并為其login方法設置替身 const teacherService = TestBed.get(TeacherService) as TeacherService; spyOn(teacherService, 'login').and.returnValue(of(true)); // 添加測試數據并調用 component.formGroup.get('username').setValue('testUsername'); component.formGroup.get('password').setValue('testPassword'); component.onSubmit(); // 斷言成功調用teacherService的login方法 expect(teacherService.login).toHaveBeenCalledWith('testUsername', 'testPassword'); }); ``` 測試通過 ### 測試console.log 剛剛的測試雖然斷言了調用的`teacherService.login`時傳入的參數是符合預期的,但是并沒有對`onSubmit`是否成功的接收到了`teacherService.login`的返回值進行斷言。在`login.component.ts`中使用了`console.log(result);`在控制臺打印了返回值,如何該斷言`console.log`語句成功執行了并且是以`teacher.login`發送的值執行的呢?若要實現斷言`console.log`,則需要為其制作替身,而制作替身的方法我們已經熟練使用了,代碼為:`spyOn(對象, 方法)`,按此思想為`console`對象制作替身如下: src/app/login/login.component.spec.ts ```javascript fit('onSubmit', () => { // 獲取teacherService實例,并為其login方法設置替身 const teacherService = TestBed.get(TeacherService) as TeacherService; spyOn(teacherService, 'login').and.returnValue(of(true)); spyOn(console, 'log'); ? // 添加測試數據并調用 component.formGroup.get('username').setValue('testUsername'); component.formGroup.get('password').setValue('testPassword'); component.onSubmit(); // 斷言成功調用teacherService的login方法 expect(teacherService.login).toHaveBeenCalledWith('testUsername', 'testPassword'); expect(console.log).toHaveBeenCalledWith(true); ? }); ``` 單元測試通過。 # 參考文檔 | 名稱 | 鏈接 | 預計學習時長(分) | | --- | --- | --- | | 源碼地址 | [https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step5.1.2](https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step5.1.2) | - |
                  <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>

                              哎呀哎呀视频在线观看