###### 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) > ![](https://i.imgur.com/VLmxpld.png) > 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) { } }; ```