###### tags: `教學`
# Night Watch
## 測試
#### 單元測試 (Unit testing)
> 以程式中最小的邏輯單元為對象,撰寫測試程式,來驗證邏輯正確與否。
> 白話來說就是以 function or method 為單位測試。
#### 整合測試 (Integration testing)
> 整合多方資源進行測試,確保模組與模組之間的互動行為正確無誤
> 例如一個訂單線上刷卡系統要同時更新訂單以及刷卡記錄,在本地資料庫中的訂單與刷卡記錄是否能成功完成更新。
#### 端對端測試 (End-to-end testing) (E2E testing)
> 「端對端」(E2E) 是指從使用者的角度出發(一端),對真實系統(另一端)進行測試。
#### 運作原理 Night Watch vs Selenium
> night watch 本身是建立在 Selenium 之上,在控制瀏覽器上使用W3C WebDriver API (前身為 Selenium)
> 
> Nightwatch 將我們所撰寫的測試程式碼轉成 HTTP Request 送到 WebDriver Server,Selenium Server 再送到瀏覽器,接著 Selenium Server 回傳 HTTP Response 回來給 Nightwatch。Nightwatch 幾乎必須至少送兩次 Request 到 Selenium Server 才能完成一個完整的指令(Command)或斷言(Assertion)。第一個 Request 是依照 CSS Selector 或 Xpath 來定位網頁元素,第二個指令是對這個網頁元素執行指令或斷言。
## GOGO
### 安裝
> 版本號很重要,建議兩個都用最新版本
```shell=
npm install nightwatch@latest
npm install chromedriver@latest
```
> 目前的版號是
```shell=
npm install nightwatch@1.1.13
npm install chromedriver@75.0.1
```
### 準備以下檔案
> 都扔在根目錄就好
#### nightwatch.js
```javascript=
require('nightwatch/bin/runner.js');
```
#### nightwatch.json
```json=
{
"src_folders" : ["./examples/tests", "./examples/unittests"],
"output_folder" : "./examples/reports",
"webdriver" : {
"start_process": true,
"server_path": "node_modules/.bin/chromedriver",
"port": 9515
},
"test_settings" : {
"default" : {
// launch_url 初始路徑
// 例如測試機跟開發機不同環境時可使用
"launch_url" : "http://localhost",
"desiredCapabilities": {
"browserName": "chrome"
},
"exclude" : "./examples/unittests/",
// 也可以替不同環境建立全域變數
"globals": {
"firstGlobalVar": "Hello World!",
"secondGlobalVar": "This is me."
}
},
"unittests" : {
"unit_tests_mode" : true,
"filter" : "./examples/unittests/",
"exclude" : ""
}
}
}
```
### 常用指令
#### click
> 點擊 class 為 button-submit 的 dom 物件
```javascript=
browser.click('.button-submit')
```
#### setValue
> 在 id 為 userid的 input text or text area 輸入 my_id
> 在 id 為 userpass 的 input text 輸入 my_password 然後鍵入 enter
> 範例為登入功能
```javascript=
browser
.setValue('#userid', 'my_id')
.setValue('#userpass', ['my_password',browser.Keys.ENTER])
```
#### getValue vs .verify.containsText()
> callback 用法
> 判斷回傳 result 是否為物件
> 判斷回傳 result 的 status 是否為 0
> 判斷回傳 result 的 value 是否為「HiHi」
```javascript=
browser
.getValue('#userid', function(result) {
this.assert.equal(typeof result, 'object');
// result.status == -1 表示 element 不存在
this.assert.equal(result.status, 0);
this.assert.equal(result.value, 'HiHi');
})
.verify.containsText('#userid', 'HiHi')
```
#### getAttribute vs verify.attributeContains()
> 判斷 id userid 的 dom class 是否為 rt-user-info-input vaild
```javascript=
browser
.getAttribute('#userid', 'class', function(result) {
this.assert.equal(result.value, 'rt-user-info-input valid');
})
.verify.attributeContains('#userid', 'class', 'rt-user-info-input valid')
```
#### getCssProperty vs verify.cssProperty
> 取得 id search_input 的 css 屬性 line-height 判斷是不是 27px
```javascript=
browser
.getCssProperty("#search_input", "line-height", function(result) {
this.assert.equal(result.value, '27px');
})
.verify.cssProperty('#search_input', 'line-height', '27px')
```
#### isVisible vs .verify.visible()
> 判斷元素是否可以直接被看到
```javascript=
browser
.isVisible('.rt-header-not-loaded', result => {
browser.assert.equal(result.value, true);
})
.verify.visible('rt-header-not-loaded')
```
#### waitForElementPresent
> 確認 DOM Element 載入完成。等待一段時間,判斷元素是否存在
#### waitForElementVisible
> 確認 DOM Element 載入完成。等待一段時間,判斷元素可以直接被看到
### cookie 系列指令
> getCookie()
> getCookies()
> setCookie()
> deleteCookie()
### window 系列指令: 視窗操作
> setWindowPosition()
> switchWindow()
> closeWindow()
> maximizeWindow()
### test hook
> 可以用於例如登入、視窗最大化之類的固定流程
> 執行的順序為
> before()
> beforeEach(), "step one", afterEach(),
> beforeEach(), "step two", afterEach(),
> after()
```javascript=
module.exports = {
before : function(browser) {
console.log('Setting up...');
},
after : function(browser) {
console.log('Closing down...');
},
beforeEach : function(browser) {
},
afterEach : function(browser) {
},
'step one' : function (browser) {
},
'step two' : function (browser) {
}
};
```