[笔记] 用Chrome Extension代替Selenium作网页自动化测试

时间:2021-12-26 20:24:28

Selenium自动化测试有诸多不稳定因素,使用起来需要看大量API文档,下载对应语言的驱动包裹。遂想直接用Chrome Extension取而代之。

如何用Chrome Extension驱动网页UI测试呢?首先我们从第一个Extension说起。

一个Extension我们放在一个文件夹里,第一个Extension有三个文件:icon.png, background.js, manifest.json

从网上有很多这样的代码,就是当特定浏览网页时,在地址栏右侧显示出extension的图标icon.png。

// background.js
function match(url){
var host = "null";
if(typeof url == "undefined" || null == url)
url = window.location.href;
var regex = /.*\:\/\/([^\/]*).*/;
var match = url.match(regex);
if(typeof match != "undefined" && null != match)
host = match[1];
return host;
}

function entry(tabId, changeInfo, tab) {
// 在URL是localhost时显示extension图标
if(match(tab.url).toLowerCase()=="localhost"){
chrome.pageAction.show(tabId);
}
};

chrome.tabs.onUpdated.addListener(entry);


// manifest.json
{
"manifest_version": 2,
"name": "Chrome Extension: Test",
"version": "0.0.1",
"background": { "scripts": ["background.js"] },
"permissions": ["tabs"],
"page_action": {
"default_icon": {
"19": "icon.png"
},
"default_title": "test extension"
}
}
接着打开Chrome,在地址栏输入chrome://extensions/,勾选Extension标题右侧“开发者模式”(developer mode)就可以看见按钮去选文件夹添加自己的extension了。

载入extension后,在新tab中输入测试页面,如http://localhost/index.html,那么地址栏的右侧便会出现icon.png的图标。

那个图标暂时就当个装饰,标明我们想要测试的页面被handle到。


下面来说一说怎么得到当前访问的页面的信息。这就要用到extension的content script特性了。在extension开发文件夹里增加一个js文件content.js:

// content.js
var links = document.getElementsByTagName('a');
var list = [];
var n,i,t;
n = links.length;
for(i=0;i<n;i++) {
t = links[i].href;
if (!t) continue;
if (t.toLowerCase().indexOf('javascript:') >= 0) continue;
list.push(t);
t = null;
}
console.log(list);

然后在manifest.json里添加content script的设置:

// manifest.json

... ...
"background": { "scripts": ["background.js"] },
// 添加content script的支持
"content_scripts":[
{"matches":["http://localhost/*"],"js":["content.js"]}
],
"permissions": ["tabs"],
... ...
把自己的extension在chrome://extensions/里重新加载(Reload)一下,再刷新一下测试页面;这时进入控制台,如果页面里有超链接,那么控制台会把它们链接到的地址全部打印出来。当然,控制台输出结果是一种形式,输出到文件也可以,我们可以把最开始那个图标用起来,点击它看结果。

实现点击图标看结果需要知道background和content script如何通信。我们先谈一谈如何将content script里得到的list让background得到,并在图标点击后显示出来。

content script中想把list交给background,需要通过消息机制实现通信。修改content.js和background.js:

// content.js
... ...
console.log(list);

chrome.runtime.sendMessage({
type: 'localtest-links',
links: list
});


// background.js
... ...
chrome.tabs.onUpdated.addListener(entry);

var pageData = {};
chrome.runtime.onMessage.addListener(function(message, sender, response){
if (message.type !== 'localtest-links') return;
pageData.links = message.links;
});
点击图标显示内容其实就是配置一个popup的页面,需要添加两个文件:popup.html和popup.js,并修改manifest.json。

<!-- popup.html -->
<html>
<body>
<div id="result">(No Result)</div>
<script type="text/javascript" src="popup.js"></script>
</body>
</html>
// popup.jsdocument.addEventListener('DOMContentLoaded', function () {  var data = chrome.extension.getBackgroundPage().pageData;  var dom = document.getElementById('result');  var html = '';  var n, i;  n = data.links.length;  for(i=0;i<n;i++) {    html += '<div>' + data.links[i] + '</div>';  }  dom.innerHTML = html;  html = null;  dom = null;  data = null;});// manifest.json... ...          "default_title": "test extension",          "default_popup": "popup.html"     }... ...
这样访问localhost的页面时,所有超链接信息都会被收集到,并在点击extension图标后显示在弹出的小窗口中。

上面整套流程基本就可以运行一些简单的测试了,测试人员可以使用脚本打开chrome,然后让extension自动执行测试,最后extension图标可以让测试人员看到测试结果。

下面就让我们想一想用extension测试时,把控制面板放入popup.html中;这里我们添加一个按钮,点击这个按钮后,会触发被测试页面中的第一个button的click单击事件。

先脑补一下事件的触发:

function trigger(element, eventType) {
var event = document.createEvent('HTMLEvents');
event.initEvent(eventType, true, true);
element.dispatchEvent(event);
event = null;
}
一个控制按钮要加到popup.html中,接着就是处理从popup中发送信息告知content script我要触发button的click事件:

<!-- popup.html -->
... ...
<div id="result">(None)</div>
<button id="fire">Fire</button>
<script src="popup.js"></script>
... ...
// popup.js... ...  dom.innerHTML = html;  dom = document.getElementById('fire');  dom.addEventListener('click', function () {    chrome.tabs.query({active: true, currentWindow: true}, function(tabs){      chrome.tabs.sendMessage(        tabs[0].id,        {type: 'localtest-fire'},        function(response) {});    });  });  html = null;... ...// content.js... ...chrome.extension.onMessage.addListener(function(message, sender, response) {  if(message.type!=='localtest-fire') return;  var buttons = document.getElementsByTagName('button');  if(buttons.length > 0) {    // trigger就是刚才脑补的那部分 :P    trigger(buttons[0], 'click');  }  buttons = null;});
为了实验效果,我们可以在本地服务器上放一个test.html文件:

<html>
<body>
<a href="http://www.baidu.com"">Hello</a><br/>
<a href="javascript:alert('hi');">World</a><br/>
<a href="test.html">self</a><br/>
<button onclick="javascript:alert('trigger')">From Popup</button>
</body>
</html>
重新加载extension后,当我们访问http://localhost/test.html时,点击地址栏右边extension的图标,点击Fire按钮,这样就会有alert弹出,上面写着"trigger"!

但是,自动测试的时候出现alert,测试就暂停了,这个是令人抓狂的,我们得屏蔽掉alert。下面就说一说content script向当前页面注入代码,目标是干掉alert。

我们知道window.alert就是alert,执行它会弹出一个消息框。只要能重载这个函数,让它把消息输出到console上就好了。需要修改content.js:

// content.js
... ...
var script = document.createElement('script');
script.type = 'text/javascript';
// 将alert替换,直接将msg输出到console
script.textContent = 'window.nativeAlert=window.alert;window.alert=function(msg){console.log(msg);};';
// 将代码注入到当前页面中
(document.head || document.documentElement).appendChild(script);
// 可以让代码运行完后再把script从页面移除,不过这个就不很重要了
script = null;
重载extension,访问test.html时,直接单击“From Popup”按钮就可以发现消息已经输出到console了,点击popup里的“fire”按钮效果同样。

当然,还可以用这种注入的方式把测试脚本加载上运行,然后返回执行结果,这样就可以做成不错的网页UI自动化测试工具了。

对于HTTPS,有时候我们会遇到弹出输入验证对话框,那我们就可以启用extension的webRequest功能,在Header中加入Authentication的信息就可以避开这个对话框的出现。

好了,现在网页UI自动化测试基本可以跑自己的extension了,写写代码还能把结果输出成文本,够用了~就不用对着selium各种神伤了。


J.Y.Liu

2014.09.15