使用 ava 和 jsdom 作前端測試

2016-09-03 更新

隨着在工做學習中更多地接觸、使用測試工具,發現本身在本文中的一些記錄是不許確、不正確的。javascript

今天(九月三日)在家看了 NingJs 的直播,其中有一個分享是關於測試框架的,很是棒,以後有可能的話仍是找來視頻再學習下。css

是的,兩個月前的理解,是很初級很淺陋的。html

繼續學習,繼續鑽研吧。java

交代前因

前些天接手了一箇舊項目。幸虧不是在原來的基礎上作些修修改改的工做,能夠算是開發新版的。jquery

把前面同事留下來的代碼 down 下來,看了一下。整體仍是挺好的。還有 macha + chai 的測試目錄。git

我也是最近一段時間開始接觸測試。好久以前看了阮大神寫的 mocha 教程,不過也就看看,寫寫簡單的 demo。github

前同事留下的測試,是基於瀏覽器的,主要仍是功能測試。這裏不詳細說怎麼在瀏覽器端使用 mocha 測試了。由於涉及到交互的反饋、追蹤,因此採用的方式是,先用 iframe 加載待測頁面,而後用 contentWindow 的方式拿到 iframe 的環境,再作一些操做。手動觸發一些功能,而後再去判斷相應的變化有沒有發生。npm

本地啓動了一個 server,瀏覽器裏跑了幾遍測試。最後發現的問題是,有一個點擊測試怎麼都過不了。因而又開啓了閱讀代碼的過程。json

最後發現了問題所在,頁面使用的是本身封裝的 tap 事件,整個事件系統也是對原生 Element 原型的拓展。但是怎麼觸發 tap 呢?前同事用了 touchend。但是並無用啊, tap 事件的觸發但是結合了從 touchstart 開啓一系列事件參數的判斷的。瀏覽器

後來我就想,瀏覽器端功能測試,能不能也拿到命令行上面來呢?

從 mocha 轉到 ava

正在此時,我想起了 jsdom 這個大神級做品。

一開始打算用 mocha + jsdom 跑一把。折騰了幾回發現,mocha 這傢伙很差適應異步的工做,這事情很難搞啊。

可能要交代下我作了什麼,嗯,我加載了一個 jquery 腳本,這樣就得外部文件,因而就有異步場景了。試了好多遍,mocha 仍是沒能實現個人指望。(你也能夠拿 mocha 試試看,多試幾回,若是單純靠那個 done 你就能成功,那麼請私信我喲。)

又想一想白天亂逛 github 的時候,在一些個項目中看到了 ava 這個測試工具。搜索一番,聽說正適用於異步場景。

好,那就來試試看唄。前因交代清楚了,下面開始正式進入教程階段。

開始講 demo

我將本身的 demo 放到了 github 上,地址是https://github.com/AngusFu/jsdom-ava-demo。你能夠直接克隆項目,而後在本地跑起來。

由於是 demo,項目內容很簡單,兩個 js,一個用於測試 html 文件。

測試場景

先說測試場景:頁面上有一個紅色背景的 div,經過原生的 addEventListener 綁定了 click 事件。點擊以後,將背景色變換爲綠色。就醬簡單?對,主要就這個,一方面我是想測試下 jsdom 對事件系統和 css 解析的支持(手動觸發事件,css 解析和值變化),一方面是想試試這種異步場景下怎麼更好地測試。

那些對測試腳本運行速度有很是嚴格要求的同窗請想好了再日後看。由於根據個人經驗,jsdom + ava 這倆組合起來,速度確實慢得不行。我還沒仔細探究緣由,但想來無非如下幾點:

  • 測試腳本要通過 babel 6 編譯一遍,有耗時;

  • jsdom 系統比較龐大,解析起來費勁;

  • 我使用了 jsdom 的 jQueryify 方法從外部加載了 jQuery 文件(但這方法確實給力);

  • ava 自己其餘方面的問題;

暫且忍着點。

核心 html 以下:

<style>
    div {width: 500px; height: 500px; background-color: red;}
</style>
<div id="div"></div>

<script>
    document.getElementById("div").onclick = function () {
        this.style.backgroundColor = 'green';
    };
</script>

測試工具安裝

下面來談工具的安裝。

首先安裝 jsdom,這卻是很簡單:

$ npm install --save jsdom

接着安裝 ava,最好先全局安裝一遍:

$ npm install -g ava

$ npm install --save ava

而後爲了方便使用 npm test 命令,執行下面的命令:

$ ava --init

這一行的目的是將 ava 命令放到你的 package.json 中的 scripts 字段中,方便以後使用 npm test 直接開啓跑測試。固然你也能夠無論這一步,我就比較喜歡本身敲 ava xx.js 這樣子。

編寫測試

好了,環境安裝完畢。下面來看腳本。

import fs from 'fs';

import { jsdom } from 'jsdom';

import test from 'ava';

ava 在運行時會經過 babel 6 對測試腳本進行編譯,所以徹底能夠自由發揮,generator、async & await 什麼的都盡情地用吧。並且做者也是建議和支持這樣作的,簡單明瞭的測試腳本,重要性有時候可能和測試自己同樣重要。

引入 fs 是爲了讀取咱們的 html 文件。

關於 jsdom 的用法,更多的能夠參考 https://github.com/tmpvar/jsdom,看項目的文檔。這裏我使用的是簡單易懂的 require('jsdom').jsdom 形式,便於以同步的形式解析生成咱們須要的 window 對象,以下:

var window = jsdom(fs.readFileSync('./test.html')).defaultView;

一個挺好用的方法是 jsdom.jQueryify,能向頁面注入 jQuery。不過這是個異步的方法(廢話),因此這裏我使用了 Promise,也是爲了方便以後使用 async & await 語法。

function jsdomTest() {
    return new Promise(function (resolve, reject) { 
        jsdom.jQueryify(window, "http://apps.bdimg.com/libs/jquery/2.1.4/jquery.js", function () {
            resolve(window.jQuery);
        });
    });
}

ava 的測試用例寫起來也挺簡單,來看代碼:

test('點擊測試', async t => {
    var $ = await jsdomTest();

    var $div = $('#div');

    var colorBeforeClick, colorAfterClick;

    console.log(colorBeforeClick = $div.css('background-color'));

    $div.trigger('click');

    console.log(colorAfterClick = $div.css('background-color'));
    
    t.not(colorBeforeClick, colorAfterClick, 'bgColor changed');
});

test 的第一個參數是測試用例的名稱,第二個參數是一個函數,該函數會注入 t 對象。咱們全部的斷言都是經過這個注入的 t 進行的。

友情提示:ava 的文檔地址,https://github.com/avajs/ava,也有中文版,可是沒更新同步,因此建議仍是看英文,不然用了一些過期的 API,之後升級以後追悔莫及。

來講上面的代碼。首先咱們使用的是 async & await 語法,整個看起來比回調函數嵌套要整潔許多,整個流程看起來也相對清楚。

第一步是先等待 jQuery 注入成功,拿到 $。其實這一步無關緊要,我純粹是爲了測試 jsdom API,而且懶得手動寫 dispatch 事件的代碼才這麼幹的。

接下來就開始 DOM 查詢,而後先獲取 div 當前的背景色並打印出來。接着手動觸發 click 事件,而後再次獲取 div 的背景色並打印。最後將觸發點擊先後的兩個顏色值拿來對比。

依葫蘆畫瓢,差很少就這麼搞定了。

打開命令行,進入工做目錄,而後開始測試:

$  ava -v parallel.js

相信我,-v 參數可讓你的命令行界面顯得比較安靜一些。

若是你想要使用 npm test 這樣的命令來測試,請進一步閱讀文檔進行相關配置(將上面的 ava 換成 nom test是沒用的哦)。這裏主要仍是爲了簡便。

友情提示

友情提示第二波:會不會懷疑,觸發點擊事件以後,顏色立馬就變了?不存在延遲、異步麼?答案是 yes,真的不存在。假如你和我同樣在這裏猶豫了,那麼說明存在這樣兩種可能性:

  • js 基礎不夠牢,對相關機制的瞭解還不透徹

  • 你被各類異步玩怕了(hybrid / RN 後遺症 )

當時爲了應對「潛在的異步」(啊我想到了迫害狂想症),我特地作了幾百毫秒的 setTimeout 延時。結果呢,斷言的謂詞(not、same、notSame等等)各類正向、反向都試了一遍,測試永遠經過。什麼鬼?說好的良好的異步支持呢?後來再去看文檔,發現人家寫得清清楚楚:

You must define all tests synchronously. They can't be defined inside setTimeout, setImmediate, etc.
全部測試必須同步定義。不能放在 setTimeout、setImmediate 等方法裏面。

因此,真的,認真讀文檔是頗有必要的。

真正遇到要延時的,怎麼辦?我想,Promise 會解救你的。

並行與串行

ava 聲稱是很高效的。一般狀況下,同一個文件裏測試都是並行的,並不必定按照順序執行。

還以上面的代碼爲例。爲了測試一下,我選擇了投機取巧。不是並行嗎?那我就檢測 jQuery 存不存在就不行了嗎?由於咱們的 test 中,是異步加載 jQuery 的。因此若是測試是並行的,那麼不必定可以檢測到 window.$ 的存在。

因此就有了 parallel.js 這個文件。添加的測試用例以下:

test('串行測試', function (t) {
    console.log(window.$)
    t.true(!!window.$, '串行失敗');
});

但是,若是我就要串行呢?

還好做者也想到了這種狀況。將全部的 test 改爲 test.serial 便可(見 serial.js)。

須要說明的是,所謂的串行執行,只是在同一個測試文件中存在,同時測試多個文件的時候,就整體而言仍然是並行的。

結尾

ava 還有不少的用法和須要注意的地方。最好的辦法仍是看文檔,而後本身寫 demo,反覆領會,並應用在實際業務中。

上面提到的內容,可能有很多錯誤。但願懂行的大神們可以提出來。

忽然想到古人說,「苟日新,又日新,日日新」。

雖然通過今人考證,這也許只是相似甲骨文的祭祀記錄的誤讀。但幾千年來,這種「新」的精神始終在。

程序世界裏,變化更是無時不在。今天的工具,明天也許就會被淘汰。

其實說到底,可以解決需求,可以方便高效使用的,纔是最好的。

向作出這些工具的大神們致敬。

更新

a 標籤點擊事件的坑

a 標籤的點擊事件用了事件代理,而後經過手動觸發無效。

經測試,在瀏覽器也有這種問題。

解決辦法是直接使用 $('a')[0].click(),原生的 click 方法比較靠譜。

參考: http://stackoverflow.com/questions/773639/how-can-i-simulate-an-anchor-click-via-jquery

相關文章
相關標籤/搜索