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= 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。
(No Result)
// popup.js
document.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' + data.links[i] + '
上面整套流程基本就可以运行一些简单的测试了,测试人员可以使用脚本打开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事件:
... ...
(None)
... ...
// 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文件:
Hello
World
self
重新加载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