在頁面上經常會有某些 DOM 因為某些條件下消失或是出現,我們接下來就會以Login Component 在使用者登入後顯示"登出" 按鈕的 DOM 元件做測試。
由於我們之前並沒有實做登入後的畫面,所以我們首先修改 login.component.html 內容加入一個簡單的登入後畫面。
我們使用 angular 提供 *ngIf 在 Html 作為控制顯示登入後與登入前的畫面,所以要稍微修改原本的 form 表單,並且加入登入後的畫面程式碼在form 表單parent 之下。
程式碼大致如下:
login.component.html
這邊我用一個變數 isAuthenticated 來存放 service.isAuthenticated() 回傳登入是否成功的 boolean 資料,並且Html 的 *ngIf 也是透過這個變數來檢查該切換到哪個畫面。
login.component.ts
除了新增 isAuthenticated 變數外,還要修改 onSubmit() 這個當使用者按下登入的處理 function。
login.component.ts
接著加入登出的處理。
login.component.ts
這樣子基本上就可以有登入後的簡易顯示使用者的email 並且還有一個『登出』的按鈕了。
登入前:
登入後:
我要測試的項目以以下兩種:
一樣我們在 login.component.spec.ts 額外寫一個 describe() 來為這次的 detect change 做撰寫測試內容。以下是基本的測試環境建立。
這邊我們注意到一個新的 Type 名稱: DebugElement ,這是一個在 @angular/core 裡提供物件。所以我們要 import DebugElement 進來。
接著我們來看看第一項測試的程式碼要如何寫。
這邊我們可以看到 DebugElement 的使用上也跟我們在使用 Angular 原本的 nativeElement 用法一樣,可以使用 querySelector() 來 query 我們要的 DOM 並透過回傳的物件作為我們測試的用途。
再來是第二項的測試,這邊必須用到上一章節提到的 fixture.detectChanges() 作為我們執行 submit 後要求 Angular 去做 data binding 跟畫面上的變化。
此外我們使用的是 Reactive Form 的表單,所以在手動給予 Email 跟 Password 的時候記得要用 patchValue() 的方式。
這樣就完成了我們在使用者登入後要能看見使用者的 Email 、登出按鈕與登入按鈕必須消失的畫面。
------- 待續
由於我們之前並沒有實做登入後的畫面,所以我們首先修改 login.component.html 內容加入一個簡單的登入後畫面。
加入登入後畫面
我們使用 angular 提供 *ngIf 在 Html 作為控制顯示登入後與登入前的畫面,所以要稍微修改原本的 form 表單,並且加入登入後的畫面程式碼在form 表單parent 之下。
程式碼大致如下:
login.component.html
<section class="loginData col-xs-12 col-sm-12 col-md-12 col-lg-12"
id="loginData" *ngIf="!isAuthenticated" >
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()"
id="loginForm">
<div class="form-group input-group col-xs-12 col-sm-12
col-md-12 col-lg-12">
<input type="email" name="email" id="email"
formControlName="email"
class="form-control email" placeholder="電子郵件(用於帳號登入)">
</div>
<div class="form-group input-group
col-xs-12 col-sm-12 col-md-12 col-lg-12">
<input type="password" name="password" id="password"
formControlName="password"
class="form-control password" placeholder="密碼">
</div>
<button type="submit" class="btn btn-primary col-xs-12
col-sm-12 col-md-12 col-lg-12"
[disabled]="loginForm.invalid">
登入
</button>
</form>
</section>
<section class="logout col-xs-12 col-sm-12 col-md-12 col-lg-12"
id="logout" *ngIf="isAuthenticated" >
<h3>你好:
<span id="userEmail">{{this.loginForm.value.email}}</span>
</h3>
<button class="btn btn-primary col-xs-12 col-sm-12 col-md-12 col-lg-12"
(click)="logout()" id="logoutBtn">登出</button>
</section>
....... 略 .......
修改程式邏輯部份
接下來是修改程式邏輯部份,簡易的判斷使用者是否有登入並且切換登入後的畫面。這邊我用一個變數 isAuthenticated 來存放 service.isAuthenticated() 回傳登入是否成功的 boolean 資料,並且Html 的 *ngIf 也是透過這個變數來檢查該切換到哪個畫面。
login.component.ts
export class LoginComponent implements OnInit {
public loginForm: FormGroup;
public isAuthenticated = false; // 切換 登入前/登入後 頁面使用
constructor(
private fb: FormBuilder,
private demo: DemoService
) { }
..... 略 .......
除了新增 isAuthenticated 變數外,還要修改 onSubmit() 這個當使用者按下登入的處理 function。
login.component.ts
onSubmit() {
localStorage.setItem('token', JSON.stringify({
email: this.loginForm.value.email}));
if (this.demo.isAuthenticated()) {
this.isAuthenticated = true;
} else {
this.isAuthenticated = false;
}
}
接著加入登出的處理。
login.component.ts
logout() {
localStorage.removeItem('token');
this.isAuthenticated = false;
}
這樣子基本上就可以有登入後的簡易顯示使用者的email 並且還有一個『登出』的按鈕了。
登入前:
登入後:
撰寫測試
接來回到 login.component.spec.ts 開始著手撰寫測試內容。我要測試的項目以以下兩種:
- 登入前要有登入按鈕、登入按鈕文字為"登入"、登出按鈕是消失的。
- 登入後要有登出按鈕、要顯示使用者的email、登入按鈕是消失的。
大概就是這兩項。
一樣我們在 login.component.spec.ts 額外寫一個 describe() 來為這次的 detect change 做撰寫測試內容。以下是基本的測試環境建立。
// detect change
describe('LoginComponent login', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
let service: DemoService;
let el: DebugElement; // 要使用它來 query DOM
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [ LoginComponent ],
imports: [ ReactiveFormsModule ],
providers: [ DemoService ]
});
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
service = TestBed.get(DemoService);
el = fixture.debugElement; // debugElement
fixture.detectChanges();
});
});
這邊我們注意到一個新的 Type 名稱: DebugElement ,這是一個在 @angular/core 裡提供物件。所以我們要 import DebugElement 進來。
// for testing change detection.
import { DebugElement } from '@angular/core';
DebugElement :
這是Angular 提供用來 query 測試時產生出來的 DOM 或是 頁面上的 elements,熟知 Angular 的人知道我們通常會用 fixture.nativeElement 這個物件來 query 我們 app 前端的 DOM 與 elements。實際上在測試的時也是可以用原本的 fixture nativeElement 去 query 前端的 DOM 並且用他做一些判斷存不存在或是修改文字、css....等 功能,但是為什麼 Angular 還要提供 DebugElement呢?主要是因為原本的 nativeElement 是依靠運行時的環境!但是你測試的時候可能是在沒有瀏覽器的 server 端做測試( 持續交付的流程下會採用 Jenkins、Travis或是CircleCI )這個時候就 沒有真的 DOM 可以使用 HTML api 去 query 。
所以 Angular 依靠 DebugElement 抽象化物件來取代建立真正的 HTML elements tree 。Angular 建立 DebugElement tree 來包裝各個原生平台的 elements tree 並且安全支持在各種平台上。
所以 Angular 依靠 DebugElement 抽象化物件來取代建立真正的 HTML elements tree 。Angular 建立 DebugElement tree 來包裝各個原生平台的 elements tree 並且安全支持在各種平台上。
接著我們來看看第一項測試的程式碼要如何寫。
it('Should get login button and without logout button \
before authenticated user.', () => {
const loginBTN = el.nativeElement.querySelector('button[type="submit"]');
const logoutBTN = el.nativeElement.querySelector('#logoutBtn');
// 有 login 按鈕
expect(loginBTN).toBeTruthy();
// 登入按鈕文字為 "登入"
expect(loginBTN.textContent.trim()).toBe('登入');
// 沒有登出按鈕
expect(logoutBTN).toBeNull();
});
這邊我們可以看到 DebugElement 的使用上也跟我們在使用 Angular 原本的 nativeElement 用法一樣,可以使用 querySelector() 來 query 我們要的 DOM 並透過回傳的物件作為我們測試的用途。
再來是第二項的測試,這邊必須用到上一章節提到的 fixture.detectChanges() 作為我們執行 submit 後要求 Angular 去做 data binding 跟畫面上的變化。
此外我們使用的是 Reactive Form 的表單,所以在手動給予 Email 跟 Password 的時候記得要用 patchValue() 的方式。
it('should show user email, logout button and \
without login button after authenticated.', () => {
const testEmail = 'clover@example.com';
const testPass = 'abcd1234';
spyOn(service, 'isAuthenticated').and.returnValue(true);
component.loginForm.patchValue({email: testEmail, password: testPass});
component.onSubmit();
fixture.detectChanges(); // 重要!
const userInfo = el.nativeElement.querySelector('#userEmail');
const loginBTN = el.nativeElement.querySelector('button[type="submit"]');
const logoutBTN = el.nativeElement.querySelector('#logoutBtn');
// 顯示使用者的 email
expect(userInfo.textContent.trim()).toBe(testEmail);
// 有登出按鈕
expect(logoutBTN).toBeTruthy();
// 沒有登入按鈕
expect(loginBTN).toBeNull();
});
這樣就完成了我們在使用者登入後要能看見使用者的 Email 、登出按鈕與登入按鈕必須消失的畫面。
------- 待續
- Angular Testing 筆記 --- 1 基礎介紹。
- Angular Testing 筆記 --- 2 測試程式的架構邏輯 Jasmine
- Angular Testing 筆記 --- 3 測試 service 元件。
- Angular Testing 筆記 --- 4 測試 pipes 元件。
- Angular Testing 筆記 --- 5 使用 Mock 與 Spy。
REF:
留言
張貼留言