前端測試主要分五大方向測試,而這五大方向也分不少小方向測試,首先簡單的介紹每一個方向的概念css
界面樣式測試html
功能測試前端
多瀏覽器測試python
性能測試git
質量測試github
如上圖所示,真正工做中沒法所有知足以上條件,因此須要做出權衡,通常來講,只須要知足如下幾點,就能夠對項目開展自動化測試(1.需求穩定
不會頻繁變動。2.多平臺運行,組合遍歷型,大量的重複任務。3.軟件維護週期長,有生命力。4.被測系統開發較爲規範,可測試性強。):web
自動化測試最大的挑戰就是需求的變化,而自動化腳本自己就須要修改、擴展、debug,去適應新的功能,若是投入產出比過低,那麼自動化測試也失去了其價值和意義;chrome
折中的作法是選擇相對穩定的模塊和功能進行自動化測試,變更較大、需求變動較頻繁的部分用手工測試;npm
測試數據、測試用例、自動化腳本的重用性和移植性較強,下降成本,提升效率和價值;api
自動化測試的需求穩定性要求、自動化框架的設計、腳本開發與調試均須要時間,這其實也是一個軟件開發過程,若是項目週期較短,沒有足夠的時間去支持這一過程,那自動化測試也就不須要了;
主要出於這幾點考慮:被測試系統的架構差別、測試技術和工具的適應性、測試人員的能力可否設計開發出適應差別的自動化測試框架;
對於界面佈局,傳統的測試都是由人工對比設計圖和產品界面。當界面有修改以後,再由人經過肉眼去檢查修改(包括正確的和錯誤的修改),這樣即費時並且測試結果又不穩定,由於人工對比測試存在兩個巨坑:1.效率低;2.人的不肯定性。對於擁有大量複雜界面的Web應用,界面佈局的測試的數量巨大,再加上這兩個問題,致使這類應用的界面佈局測試/迴歸測試時間很長,成本很高,因此不少基於Agile(敏捷開發)項目基本不可能在迭代週期內高質量的完成其視覺測試。對於天天作一次,那更是不可能完成的任務。
爲了解決上面提到的各類問題,視覺感知測試孕育而生。它使用傳統的對圖片進行二進制比較的辦法,結合敏捷迭代開發的理念,產生的一種針對界面佈局的自動化測試方法。
視覺感知測試就是對第一個版本的全部界面進行第一次測試。
視覺感知測試包含如下幾個主要的測試步驟:
須要注意的是!
經過配對URL,對全部的截圖按照相同的URL進行分組。固然有時候會出現新的界面,有時候老的界面會被刪除。對於新的界面就須要人工進行首次驗證測試 。
對於分組以後的截圖進行像素級別的比較並生產差異圖。有時候爲了降噪,能夠只對局部關心的組件進行比較。
最後經過人工審查差異圖報告完成測試。
視覺感知測試結果:
預期(expected) | 實際(actual) | 比較結果(diff) |
---|---|---|
咱們認爲若是一個界面經過第一次的人工驗證併發布以後,它就是一個正確的標準界面,而且是包含了人工測試價值的資產。當下一次測試的時候,這部分價值就應該被保留並重用起來,用於減小新的一次測試的時間,從而實現界面的快速回歸測試。
視覺迴歸測試包含如下幾個主要的測試步驟:
迴歸和感知測試流程差很少只是差別值要更小一點,而且只有效果圖須要替換內容。
要進行視覺自動測試,有三種方式。
這三種各有明顯的優點和不足。 第二種方式強綁定了實現,從而變得可能比較脆弱。 第一種方式離設計太近了,當頁面中有可變內容時就會有問題。
第三種方式,沒法進行視覺感知測試結果只能進行視覺迴歸測試和上一版的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???我是視覺測試要無界面瀏覽器幹嗎?
由於有了像素對比工具咱們還須要一個瀏覽器進行截圖和設計圖進行像素比較。
名稱 | 內核 | 地址 |
---|---|---|
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
是一個基於webkit的JavaScript API。它使用QtWebKit做爲它核心瀏覽器的功能,使用webkit來編譯解釋執行JavaScript代碼。任何你能夠在基於webkit瀏覽器作的事情,它都能作到。它不只是個隱形的瀏覽器,提供了諸如CSS選擇器、支持Web標準、DOM操做、JSON、HTML五、Canvas、SVG等,同時也提供了處理文件I/O的操做,從而使你能夠向操做系統讀寫文件等。PhantomJS的用處可謂很是普遍,諸如網絡監測、網頁截屏、無需瀏覽器的 Web 測試、頁面訪問自動化等。
可是 PhantomJS
由於畢竟不是真實的用戶瀏覽器環境,使用起來仍是有很多的詬病。以前一直在使用 PhantomJS
,功能雖然夠用,不過和在真實的瀏覽器裏面訪問的界面來對比差異仍是比較大的。
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瀏覽器 |
---|---|---|
瀏覽器效果(大圖):
好了看到這裏已經能夠分別出來勝負了。
咱們來作一個簡單的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測試指的是測試界面樣式而不是去匹配裏面的內容,若是不替換裏面的內容那像素對比工具比較出來的類似度確定很低。因此咱們要替換掉內容,要讓內容完整統一,咱們纔好更加精確的去比較差別。
好了咱們來分析一下要替換的內容。
要替換的內容以下:
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(); })();
差別圖
有差別 | 無差別 |
---|---|
git完整代碼:https://github.com/my07ke/Per...
好了,欲知後事如何,請聽下回分解。
點擊連接加入羣聊【前端|WEB|CSS|Javascript|HTML】:https://jq.qq.com/?_wv=1027&k...