如何使用 Protractor 寫 Select 的驗收測試 ?
HTML 的 <select>
是常見的控制項,該如何使用 Protractor 對 select 寫驗收測試呢 ?
Version
Protractor 5.1.2
Requirement
畫面只有 1 個 <select>
,下方會顯示目前所選擇的 value。
<select>
下共有 3
個 option,各為 AWS
、Azure
與 GCP
。
當選擇 AWS
時,下方顯示 0
。
當選擇 Azure
時,下方顯示 1
。
當選擇 GCP
,下方顯示 2
。
Acceptance Test (紅燈)
測試案例 :
- 應該有
1
個<select>
- 應該有
3
個<option>
- 當選擇
AWS
,下方應該出現0
- 當選擇
Azure
,下方應該出現1
- 當選擇
GCP
,下方應該出現2
src/app.e2e-spec.ts
1 | import { AppPage } from './app.po'; |
11 行
1 | it(`should have '1' select`, () => { |
測試案例 : 應該有
1
個<select>
若 <select>
存在,則 isPresent()
回傳 true
,否則回傳 false
。
page.getSelect()
將由 page object 傳回 ElementFinder
型別且符合條件的 <select>
。
15 行
1 | it(`should have '3' options in select`, () => { |
測試案例 : 應該有
3
個<option>
測試 <select>
是否有 AWS
、Azure
與 GCP
3 個 <option>
,實務上這些資料會從後端 API 來,可測試 API 是否正常,也可測試 API 的 SQL 或 ORM 是否正確。
若要測試資料完全相同比較困難時,最少可測試資料的筆數是否正確。
page.getSelectCount()
將由 page object 傳回 <option>
的個數。
19 行
1 | it(`should show '0' when selecting 'AWS'`, () => { |
測試案例 : 當選擇
AWS
,下方應該出現0
要模擬 user 選擇 <select>
,有 2
種方式,一種是使用 index 選擇,一種是由文字選擇。
page.selectCloudByIndex()
將使用 index 方式選擇 <select>
。
24 行
1 | it(`should show '1' when selecting 'Azure'`, () => { |
測試案例 : 當選擇
Azure
,下方應該出現1
page.selectCloudByText()
將使用文字方式選擇 <select>
。
src/app.po.ts
1 | import { browser, by, element } from 'protractor'; |
*.e2e-spec.ts
負責描述測試案例,不包含 HTML 與 CSS 部分。
*.po.ts
則負責描述 HTML 與 CSS 部分。
Page object 主要目的在於讓測試與 HTML/CSS 解耦合,不要 designer 若變動了 HTML 或 CSS,則所有驗收測試都要修改,只要修改 page object 即可。
驗收測試應該只根據需求變動而修改,不應該因為 HTML/CSS 變動而修改。
第 8 行
1 | getSelect(): ElementFinder { |
根據 id 為 TDDSelect
回傳 ElementFinder 物件,主要是為了 isPresent()
判斷 <select>
是否存在。
12 行
1 | getSelectCount(): any { |
回傳 <select>
下共有幾個 <option>
。
若要在 HTML 的階層下找單一 element,可用 element(locator).element(locator)
。
若要在 HTML 的階層下找多個 element,可用 element(locator).all(locator)
。
因為 all()
是陣列,可用 count()
得知陣列筆數。
count()
的型別不是number
,而是wdpromise.Promise<number>
,因為型別比較複雜,所以迴船型別使用any
代替。
18 行
1 | selectCloudByIndex(index: number): AppPage { |
使用 index 選擇 <select>
。
先用 element(by.id('TDDSelect'))
找到 <select>
,由 all(by.tagName('option'))
取得 <select>
下所有 <option>
的陣列,再透過 get(index)
選擇 <select>
的 <option>
,最後 click()
。
27 行
1 | selectCloudByText(text: string): AppPage { |
使用文字選擇 <select>
。
先用 element(by.id('TDDSelect'))
找到 <select>
,因為要直接由文字得到單一 <option>
,就不再使用 all()
,直接使用 element()
。
透過 by.cssContainingText('option', text)
取得 <option>
,最後 click()
。
因為我們還沒實作任何功能,得到預期的驗收測試 紅燈
。
Acceptance Test (綠燈)
測試案例 :
- 應該有
1
個<select>
- 應該有
3
個<option>
- 當選擇
AWS
,下方應該出現0
- 當選擇
Azure
,下方應該出現1
- 當選擇
GCP
,下方應該出現2
src/app/app.component.html
1 | <select id="TDDSelect" (change)="onChange(mySelect)" #mySelect> |
1 | <select id="TDDSelect" (change)="onChange(mySelect)" #mySelect> |
將 change
event 綁定到 onChange()
,並將 #mySelect
傳入 onChange()
。
1 | <option *ngFor="let cloud of clouds" [value]="cloud.id">{{ cloud.name }}</option> |
使用 *ngFor
根據 clouds
field 產生 <option>
。
src/app/app.component.ts
1 | import { Component } from '@angular/core'; |
第 10 行
1 | selectedId = '0'; |
selectedId
field 預設為 0
。
11 行
1 | clouds: Cloud[] = [ |
建立 cloud
field 與設定初始陣列。
17 行
1 | onChange(element: HTMLSelectElement) { |
將 HTMLSelectElement.value
值指定到 selectedId
field。
功能都實作出來了,重新跑一次驗收測試確認都為 綠燈
。
Conclusion
- 實務上整個 ATDD 循環應該是驗收測試 (紅燈) -> 整合測試 (紅燈) -> 單元測試 (紅燈) -> 單元測試 (綠燈) -> 整合測試 (綠燈) -> 驗收測試 (綠燈),因為本文重點在於 Protract 驗收測試的
<select>
寫法,所以省略了整合測試與單元測試部分。 - 本文特別針對
<select>
展示了selectCloudByIndex()
與selectCloudByText()
兩種寫法。
Sample Code
完整的範例可以在我的 GitHub 找到。