前端自動化測試 之 視覺測試

原文鏈接:https://segmentfault.com/a/1190000014720175

前端測試分類

5ad6b227e0634.png

前端測試主要分五大方向測試,而這五大方向也分很多小方向測試,首先簡單的介紹每個方向的概念

  1. 界面樣式測試

    • 固定界面樣式測試:主要針對文字內容不變的區域,例如頁面的頁頭,頁腳這類結構、內容不變的區域,而測試一般通過截圖對比解決。
    • 結構不變界面樣式測試:主要針對結構不變的區域,例如新聞區域這類結構不變,內容變化的區域,這類測試一般通過DOM元素對比解決。
    • 計算樣式測試:主要針對計算樣式不變的區域,這類測試一般通過比較計算樣式解決,但是這種測試不推薦,因爲測試成本比較大。
  2. 功能測試

    • 服務器數據預期測試:主要針對用戶在前端界面進行某種操作後,提交數據給後臺後,測試後臺能否返回預期的數據
    • 界面功能測試:主要針對用戶在前端界面進行某種交互性操作後,測試能否獲取預期的功能、界面交互
  3. 多瀏覽器測試

    • 多瀏覽器測試:基於界面樣式測試、功能測試的基礎上來進行不同瀏覽器的的測試,俗稱兼容性測試。
  4. 性能測試

    • 白屏時間:用戶瀏覽器輸入網址後至瀏覽器出現至少1px畫面爲止。
    • 首屏時間:用戶瀏覽器首屏內所有的元素呈現所花費時間。
    • 頁面迴歸時間:用戶瀏覽器非第一次加載所有的元素呈現所花費時間。
    • 用戶可操作時間(dom ready) :網站某些功能可以使用的時間。
    • 頁面總下載時間(onload):網站中所有資源加載完成並且可用時間。
  5. 質量測試

什麼樣的項目適合自動化測試呢?

5ae7f3fc745e7.png

如上圖所示,真正工作中無法全部滿足以上條件,所以需要作出權衡,一般來說,只需要滿足以下幾點,就可以對項目開展自動化測試(1.需求穩定
不會頻繁變更。2.多平臺運行,組合遍歷型,大量的重複任務。3.軟件維護週期長,有生命力。4.被測系統開發較爲規範,可測試性強。):

  1. 需求穩定,不會頻繁變更

    自動化測試最大的挑戰就是需求的變化,而自動化腳本本身就需要修改、擴展、debug,去適應新的功能,如果投入產出比太低,那麼自動化測試也失去了其價值和意義;

    折中的做法是選擇相對穩定的模塊和功能進行自動化測試,變動較大、需求變更較頻繁的部分用手工測試;

  2. 多平臺運行,組合遍歷型、大量的重複任務

    測試數據、測試用例、自動化腳本的重用性和移植性較強,降低成本,提高效率和價值;

  3. 軟件維護週期長,有生命力

    自動化測試的需求穩定性要求、自動化框架的設計、腳本開發與調試均需要時間,這其實也是一個軟件開發過程,如果項目週期較短,沒有足夠的時間去支持這一過程,那自動化測試也就不需要了;

  4. 被測系統開發較爲規範,可測試性強

    主要出於這幾點考慮:被測試系統的架構差異、測試技術和工具的適應性、測試人員的能力能否設計開發出適應差異的自動化測試框架;

什麼是UI測試

對於界面佈局,傳統的測試都是由人工對比設計圖和產品界面。當界面有修改之後,再由人通過肉眼去檢查修改(包括正確的和錯誤的修改),這樣即費時而且測試結果又不穩定,因爲人工對比測試存在兩個巨坑:1.效率低;2.人的不確定性。對於擁有大量複雜界面的Web應用,界面佈局的測試的數量巨大,再加上這兩個問題,導致這類應用的界面佈局測試/迴歸測試時間很長,成本很高,所以很多基於Agile(敏捷開發)項目基本不可能在迭代週期內高質量的完成其視覺測試。對於每天做一次,那更是不可能完成的任務。

視覺感知測試/視覺迴歸測試

爲了解決上面提到的各種問題,視覺感知測試孕育而生。它使用傳統的對圖片進行二進制比較的辦法,結合敏捷迭代開發的理念,產生的一種針對界面佈局的自動化測試方法。

視覺感知測試

視覺感知測試就是對第一個版本的所有界面進行第一次測試。

視覺感知測試包含以下幾個主要的測試步驟:

5ae86eda2a1cc.jpg

需要注意的是!

  1. 配對URL(忽略hostname)

    通過配對URL,對所有的截圖按照相同的URL進行分組。當然有時候會出現新的界面,有時候老的界面會被刪除。對於新的界面就需要人工進行首次驗證測試 。

  2. 像素級別的圖形比較

    對於分組之後的截圖進行像素級別的比較並生產差別圖。有時候爲了降噪,可以只對局部關心的組件進行比較。

  3. 人工查看所有不同

    最後通過人工審查差別圖報告完成測試。

視覺感知測試結果:

預期(expected) 實際(actual) 比較結果(diff)
1diff

視覺迴歸測試

我們認爲如果一個界面通過第一次的人工驗證併發布之後,它就是一個正確的標準界面,並且是包含了人工測試價值的資產。當下一次測試的時候,這部分價值就應該被保留並重用起來,用於減少新的一次測試的時間,從而實現界面的快速回歸測試。

視覺迴歸測試包含以下幾個主要的測試步驟:
5aebce31440f0.png

迴歸和感知測試流程差不多隻是差異值要更小一點,並且只有效果圖需要替換內容。

視覺自動測試怎麼做?

要進行視覺自動測試,有三種方式。

  • 第一種是截屏比對(局部、整頁)。
  • 第二種通過JavaScript調用window.getComputedStyle()獲取計算後的樣式並進行斷言。
  • 第三種dom結構對比加css樣式對比。

這三種各有明顯的優勢和不足。 第二種方式強綁定了實現,從而變得可能比較脆弱。 第一種方式離設計太近了,當頁面中有可變內容時就會有問題。
第三種方式,無法進行視覺感知測試結果只能進行視覺迴歸測試和上一版的dom繼續比較差異。

我更傾向與第一種截圖對比;它的測試基於用戶所見而不是用戶所見的抽象。當然第三種也是非常好的 page-monitor 有興趣的朋友可以自行了解。爲什麼第三種那麼好爲什麼不使用呢?因爲上面這個庫是基於phantomjs並且它的實現方式過於複雜不適合新手玩玩。

像素對比工具,有哪些?

名稱 地址
PhantomCSS https://github.com/HuddleEng/...
GhostStory https://github.com/thingsinja...
Cactus https://github.com/winston/ca...
Needle https://github.com/python-nee...
CSSCritic https://github.com/cburgmer/c...
sikuli http://www.sikuli.org/
Mogo http://mogotest.com/
pixelmatch https://github.com/mapbox/pix...
pixel-diff https://github.com/koola/pixe...

好了介紹了那麼多,怎麼選一個合適的Headless Browser呢?

Headless Browser???我是視覺測試要無界面瀏覽器幹嘛?

因爲有了像素對比工具我們還需要一個瀏覽器進行截圖和設計圖進行像素比較。

比較常見出名的幾個Headless Browser,有哪些?

名稱 內核 地址
Puppeteer Webkit https://github.com/GoogleChro...
PhantomJS Webkit http://phantomjs.org/
SlimerJS Gecko https://github.com/laurentj/s...
TrifleJS IE https://github.com/sdesalas/t...
PhantomJS 基於 Webkit 內核,不支持 Flash 的播放;SlimerJS 基於火狐的 Gecko 內核,支持 Flash播放,並且執行過程會有頁面展示。

我們這裏呢就只講Webkit內核的,其他的我就不講了。

PhantomJS簡介:

PhantomJS 是一個基於webkit的JavaScript API。它使用QtWebKit作爲它核心瀏覽器的功能,使用webkit來編譯解釋執行JavaScript代碼。任何你可以在基於webkit瀏覽器做的事情,它都能做到。它不僅是個隱形的瀏覽器,提供了諸如CSS選擇器、支持Web標準、DOM操作、JSON、HTML5、Canvas、SVG等,同時也提供了處理文件I/O的操作,從而使你可以向操作系統讀寫文件等。PhantomJS的用處可謂非常廣泛,諸如網絡監測、網頁截屏、無需瀏覽器的 Web 測試、頁面訪問自動化等。

但是 PhantomJS 因爲畢竟不是真實的用戶瀏覽器環境,使用起來還是有不少的詬病。之前一直在使用 PhantomJS ,功能雖然夠用,不過和在真實的瀏覽器裏面訪問的界面來對比差別還是比較大的。

Puppeteer簡介:

Puppeteer是Chrome團隊開發的一個Node庫。它提供了一個高級API來控制無頭或完整的Chrome。它通過使用Chrome無界面模式 (Headless Chrome )和DevTools協議的組合來實現這一點。它使用一個更上層的API來封裝其功能,讓用戶界面測試自動化變得輕而易舉。
人們基於Chrome DevTools協議開發了一系列Google Chrome工具。你在瀏覽器中點擊更多工具 ->開發工具,打開的就是DevTools。DevTools協議是DevTools的動力基礎,我們現在可以使用Chrome中的DevTools來做更多的事情。

好了簡介講完了,我們來對比一下這兩個Headless Browser的區別。

截圖比較

代碼:

PhantomJS:

var page = require('webpage').create();

page.viewportSize = { width: 400, height: 400 };
page.open("http://localhost:8899/VS", function(status) {
    if (status === "success") {
        page.render("a.jpg");

    } else {
        console.log("Page failed to load.");
    }
    phantom.exit(0);
});

Puppeteer:

const puppeteer = require('puppeteer');

(async() => {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.goto('http://localhost:8899/VS');

    await page.setViewport({ width: 400, height: 400 })

    //保存圖片
    const images = await page.screenshot({ path: 'VS.jpg', fullPage: true, omitBackground: true });

    //關閉瀏覽器
    await browser.close();

})();
Puppeteer PhantomJS Chrome瀏覽器
5ab35f3ecea48.jpg 5ab23225073a2.jpg 5ab361e464d76.png

瀏覽器效果(大圖):

5ab232251f25b.png

好了看到這裏已經可以分別出來勝負了。

使用puppeteer進行視覺感知測試

我們來做一個簡單的dome

我們這裏拿掘金來做一個視覺感知測試的例子。

1.首先創建項目名字就叫“PerceptionTest”把。

2.安裝依賴

安裝Puppeteer

npm install puppeteer --save

像素對比工具我就選我最常用的blink-dif了

npm install blink-diff --save

3.依賴安完了我們來創建一個js文件“app.js”

4.加載依賴

const puppeteer = require('puppeteer'),//無頭瀏覽器
    BlinkDiff = require('blink-diff'),//像素對比
    imgUrl = __dirname + "/blink-diff_img/";//圖片目錄

5.使用puppeteer進行截圖

(async () => {
    //創建puppeteer
    const browser = await puppeteer.launch({ headless: true });
    //new 一個新的tab頁面
    const page = await browser.newPage();
    //設置瀏覽器的尺寸
    await page.setViewport({ width: 1920, height: 945 });
    //打開url
    await page.goto('https://juejin.im/');
    //保存截圖
   await page.screenshot({ path: imgUrl + 'Screenshots.png', fullPage: true });
  
    //關閉瀏覽器
    await browser.close();
})();

6.分析頁面找到要替換的內容

爲什麼要替換內容呢,因爲我們UI測試指的是測試界面樣式而不是去匹配裏面的內容,如果不替換裏面的內容那像素對比工具比較出來的相似度肯定很低。所以我們要替換掉內容,要讓內容完整統一,我們纔好更加精確的去比較差異。

好了我們來分析一下要替換的內容。

5aeb1fa0de449.png

要替換的內容如下:

  1. 標題
  2. 標籤
  3. 作者
  4. 發佈時間
  5. 閱讀數
  6. 列表圖片

7.替換內容

puppeteer提供了非常豐富的api,其中有個api叫page.evaluate可以向頁面插入一段js。

 await page.evaluate(async () => {

        //列表
        var Lists = document.querySelectorAll("div.feed.welcome__feed > ul > li > div > a > div");
        Lists.forEach(function (element, index, array) {

            element.querySelector("a.title").innerHTML = "測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試";

            //替換標籤
            element.querySelector("ul > li.item.category > span").innerHTML = "測試";

            //替換作者
            element.querySelector("ul > li.item.username.clickable > div > a").innerHTML = "測試";

            //替換髮布時間
            element.querySelector("div.info-row.meta-row > ul > li:nth-child(3)").innerHTML = "9999天前";

            //替換髮布時間
            element.querySelector("div.info-row.meta-row > ul > li:nth-child(4)").innerHTML = "99999999999 次閱讀";

            //列表圖片
            if (element.querySelectorAll("div.lazy.thumb.thumb.loaded").length==1) {
                element.querySelector("div.lazy.thumb.thumb.loaded").style.background = "#fdedc9";
            } else {
                var loaded=document.createElement("div");
                loaded.className=" lazy thumb thumb loaded";
                loaded.style.background = "#fdedc9";
                loaded.setAttribute("data-v-b2db8566","");
                loaded.setAttribute("data-v-009ea7bb","");
                loaded.setAttribute("data-v-f2ca14b0","");
                element.appendChild(loaded);
            }
        });

    });

8.使用Blink-Diff進行像素對比較

const diff = new BlinkDiff({
        imageAPath: imgUrl + 'example.png', // 設計圖
        imageBPath: imgUrl + 'Screenshots.png',//頁面截圖
        //低於其中差異的像素數/ p(默認值:500) - 百分比閾值:1 = 100%,0.2 = 20%
        threshold: 0.02, // 1% threshold
        imageOutputPath: imgUrl + 'Diff.png'//Diff路徑
    });

    diff.run(function (error, result) {
        if (error) {
            throw error;
        } else {
            console.log(diff.hasPassed(result.code) ? '通過' : '失敗');
            console.log('總像素:' + result.dimension);
            console.log('發現:' + result.differences + ' 差異.');
        }
    });

完整代碼:

const puppeteer = require('puppeteer'),
    BlinkDiff = require('blink-diff'),
    imgUrl = __dirname + "/blink-diff_img/";

(async () => {
    const browser = await puppeteer.launch({ headless: true });
    const page = await browser.newPage();
    await page.setViewport({ width: 1920, height: 945 });
    await page.goto('https://juejin.im/');



    await page.evaluate(async () => {

        //列表
        var Lists = document.querySelectorAll("div.feed.welcome__feed > ul > li > div > a > div");
        Lists.forEach(function (element, index, array) {

            element.querySelector("a.title").innerHTML = "測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試測試";

            //替換標籤
            element.querySelector("ul > li.item.category > span").innerHTML = "測試";

            //替換作者
            element.querySelector("ul > li.item.username.clickable > div > a").innerHTML = "測試";

            //替換髮布時間
            element.querySelector("div.info-row.meta-row > ul > li:nth-child(3)").innerHTML = "9999天前";

            //替換髮布時間
            element.querySelector("div.info-row.meta-row > ul > li:nth-child(4)").innerHTML = "99999999999 次閱讀";

            //列表圖片
            if (element.querySelectorAll("div.lazy.thumb.thumb.loaded").length==1) {
                element.querySelector("div.lazy.thumb.thumb.loaded").style.background = "#fdedc9";
            } else {
                var loaded=document.createElement("div");
                loaded.className=" lazy thumb thumb loaded";
                loaded.style.background = "#fdedc9";
                loaded.setAttribute("data-v-b2db8566","");
                loaded.setAttribute("data-v-009ea7bb","");
                loaded.setAttribute("data-v-f2ca14b0","");
                element.appendChild(loaded);
            }
        });

    });

    await page.screenshot({ path: imgUrl + 'Screenshots.png', fullPage: true });

    const diff = new BlinkDiff({
        imageAPath: imgUrl + 'example.png', // 設計圖
        imageBPath: imgUrl + 'Screenshots.png',//頁面截圖
        threshold: 0.02, // 1% threshold
        imageOutputPath: imgUrl + 'Diff.png'//Diff路徑
    });

    diff.run(function (error, result) {
        if (error) {
            throw error;
        } else {
            console.log(diff.hasPassed(result.code) ? '通過' : '失敗');
            console.log('總像素:' + result.dimension);
            console.log('發現:' + result.differences + ' 差異.');
        }
    });


    //關閉puppeteer
    await browser.close();
})();

差異圖

有差異 無差異
5aeb373a1fa34.png 5aeb3773ec472.png

git完整代碼

git完整代碼:https://github.com/my07ke/Per...

好了,欲知後事如何,請聽下回分解。

5aebd8bbb1762.gif

點擊鏈接加入羣聊【前端|WEB|CSS|Javascript|HTML】:https://jq.qq.com/?_wv=1027&k...

參考:

淺談UI自動化測試

視覺感知測試

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章