技術(shù)雷達快訊:自2017年中以來谴供,Chrome用戶可以選擇以headless模式運行瀏覽器叶骨。此功能非常適合運行前端瀏覽器測試显歧,而無需在屏幕上顯示操作過程边败。在此之前,這主要是PhantomJS的領(lǐng)地棉磨,但Headless Chrome正在迅速取代這個由JavaScript驅(qū)動的WebKit方法子檀。Headless Chrome瀏覽器的測試運行速度要快得多浴韭,而且行為上更像一個真正的瀏覽器客叉,雖然我們的團隊發(fā)現(xiàn)它比PhantomJS使用更多的內(nèi)存诵竭。有了這些優(yōu)勢,用于前端測試的Headless Chrome很可能成為事實上的標準兼搏。
隨著Google在Chrome 59版本放出了headless模式卵慰,Ariya Hidayat決定放棄對Phantom.js的維護,這也標示著Phantom.js 統(tǒng)治fully functional headless browser的時代將被chrome-headless代替佛呻。
Headless Browser
也許很多人對無頭瀏覽器還是很陌生裳朋,我們先來看看維基百科的解釋:
A headless browser is a web browser without a graphical user interface.
Headless browsers provide automated control of a web page in an environment similar to popular web browsers, but are executed via a command-line interface or using network communication.
對,就是沒有頁面的瀏覽器吓著。多用于測試web再扭、截圖氧苍、圖像對比、測試前端代碼泛范、爬蟲(雖然很慢)、監(jiān)控網(wǎng)站性能等紊撕。
為什么要使用headless測試罢荡?
headless broswer可以給測試帶來顯著好處:
- 對于UI自動化測試,少了真實瀏覽器加載css对扶,js以及渲染頁面的工作区赵。無頭測試要比真實瀏覽器快的多。
- 可以在無界面的服務(wù)器或CI上運行測試浪南,減少了外界的干擾笼才,使自動化測試更穩(wěn)定。
- 在一臺機器上可以模擬運行多個無頭瀏覽器络凿,方便進行并發(fā)測試骡送。
headless browser有什么缺陷?
以phantomjs為例
- 雖然Phantom.js 是fully functional headless browser絮记,但是它和真正的瀏覽器還是有很大的差別摔踱,并不能完全模擬真實的用戶操作。很多時候怨愤,我們在Phantom.js發(fā)現(xiàn)一些問題派敷,但是調(diào)試了半天發(fā)現(xiàn)是Phantom.js自己的問題
- 將近2k的issue,仍然需要人去修復(fù)撰洗。
- Javascript天生單線程的弱點篮愉,需要用異步方式來模擬多線程,隨之而來的callback地獄差导,對于新手而言非常痛苦试躏,不過隨著es6的廣泛應(yīng)用,我們可以用promise來解決多重嵌套回調(diào)函數(shù)的問題柿汛。
- 雖然webdriver支持htmlunit與phantomjs冗酿,但由于沒有任何界面,當我們需要進行調(diào)試或復(fù)現(xiàn)問題時络断,就非常麻煩裁替。
那么Headless Chrome與上面提到fully functional headless browser又有什么不同呢?
什么是Headless Chrome貌笨?
Headless Chrome 是 Chrome 瀏覽器的無界面形態(tài)弱判,可以在不打開瀏覽器的前提下,使用所有Chrome支持的特性锥惋,在命令行中運行你的腳本昌腰。相比于其他瀏覽器开伏,Headless Chrome 能夠更加便捷的運行web自動化測試、編寫爬蟲遭商、截取圖等功能固灵。
有的人肯定會問:看起來它的作用和phantomjs沒什么具體的差別?
對劫流,是的巫玻,Headless Chrome 發(fā)布就是來代替phantomjs。
我們憑什么換用Headless Chrome祠汇?
- 我爸是Google,那么就意味不會出現(xiàn)phantomjs近2k問題沒人維護的尷尬局面仍秤。 比phantomjs有更快更好的性能。
- 有人已經(jīng)做過實驗可很,同一任務(wù)诗力,Headless Chrome要比現(xiàn)phantomjs更加快速的完成任務(wù),且占用內(nèi)存更少
(https://hackernoon.com/benchmark-headless-chrome-vs-phantomjs-e7f44c6956c)
- chrome對ECMAScript 2017 (ES8)支持我抠,同樣headless隨著chrome更新苇本,意味著我們也可以使用最新的js語法來編寫的腳本,例如async屿良,await等圈澈。
- 完全真實的瀏覽器操作,chrome headless支持所有chrome特性尘惧。
- 更加便利的調(diào)試康栈,我們只需要在命令行中加入--remote-debugging-port=9222,再打開瀏覽器輸入localhost:9222(ip為實際運行命令的ip地址)就能進入調(diào)試界面喷橙。[圖片上傳失敗...(image-a57d6f-1516155651895)]
能帶給QA以及項目什么好處啥么?
前端測試改進
以目前的項目來說,之前的前端單元測試以及組件測試是用karma在phantomjs運行的贰逾,非常不穩(wěn)定悬荣,在遠端CI上運行時經(jīng)常會莫名其妙的掛掉,也找不出來具體的原因疙剑,自從Headless Chrome推出后氯迂,我們將phantomjs切換成Headless Chrome,再也沒有出現(xiàn)過異常情況言缤,切換也非常簡單嚼蚀,只需要把karma.conf.js文件中的配置改下就OK了。如下
customLaunchers: { myChrome: { base: 'ChromeHeadless', flags: ['--no-sandbox', '--disable-gpu', '--remote-debugging-port=9222'] } },
browsers: ['myChrome'],
UI功能測試改進
原因一管挟,Chrome-headless能夠完全像真實瀏覽器一樣完成用戶所有操作轿曙,再也不用擔心跑測試時,瀏覽器受到干擾,造成測試失敗
原因二导帝,之前如果我們像要在CI上運行UI自動化測試守谓,非常麻煩。必須使用Xvfb幫助才能在無界面的Linux上 運行UI自動化測試您单。(Xvfb是一個實現(xiàn)了X11顯示服務(wù)協(xié)議的顯示服務(wù)器斋荞。 不同于其他顯示服務(wù)器,Xvfb在內(nèi)存中執(zhí)行所有的圖形操作睹限,不需要借助任何顯示設(shè)備譬猫。)現(xiàn)在也只需要在webdriver啟動時,設(shè)置一下chrome option即可羡疗,以capybara為例:
Capybara.register_driver :selenium_chrome do |app|
Capybara::Selenium::Driver.new(app, browser: :chrome,
desired_capabilities: {
"chromeOptions" => {
"args" => [ "--incognito",
"--allow-running-insecure-content",
"--headless",
"--disable-gpu"
]}
})
end
無縫切換,只需更改下配置别洪,就可以提高運行速度與穩(wěn)定性叨恨,何樂而不為。
Google終極大招
Google 最近放出了終極大招——Puppeteer(Puppeteer is a Node library which provides a high-level API to control headless Chrome over the DevTools Protocol. It can also be configured to use full (non-headless) Chrome.)
類似于webdriver的高級別的api挖垛,去幫助我們通過DevTools協(xié)議控制無界面Chrome痒钝。
在puppteteer之前,我們要控制chrome headless需要使用chrome-remote-interface來實現(xiàn)痢毒,但是它比 Puppeteer API 更接近低層次實現(xiàn)送矩,無論是閱讀還是編寫都要比puppteteer更復(fù)雜。也沒有具體的dom操作哪替,尤其是我們要模擬一下click事件栋荸,input事件等,就顯得力不從心了凭舶。
我們用同樣2段代碼來對比一下2個庫的區(qū)別晌块。
首先來看看 chrome-remote-interface
const chromeLauncher = require('chrome-launcher');
const CDP = require('chrome-remote-interface');
const fs = require('fs');
function launchChrome(headless=true) {
return chromeLauncher.launch({
// port: 9222, // Uncomment to force a specific port of your choice.
chromeFlags: [
'--window-size=412,732',
'--disable-gpu',
headless ? '--headless' : ''
]
});
}
(async function() {
const chrome = await launchChrome();
const protocol = await CDP({port: chrome.port});
const {Page, Runtime} = protocol;
await Promise.all([Page.enable(), Runtime.enable()]);
Page.navigate({url: 'https://www.github.com/'});
await Page.loadEventFired(
console.log("start")
);
const {data} = await Page.captureScreenshot();
fs.writeFileSync('example.png', Buffer.from(data, 'base64'));
// Wait for window.onload before doing stuff.
protocol.close();
chrome.kill(); // Kill Chrome.
再來看看 puppeteer
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://www.github.com');
await page.screenshot({path: 'example.png'});
await browser.close();
})();
對,就是這么簡短明了帅霜,更接近自然語言匆背。沒有callback,幾行代碼就能搞定我們所需的一切身冀。
總結(jié)
目前Headless Chrome仍然存在一些問題钝尸,還需要不斷完善,我們應(yīng)該擁抱變化搂根,適應(yīng)它珍促,讓它給我們的工作帶來更多幫助。