使用Selenium RC侦测页面的JavaScript错误
之前有同事问起Selenium是否可以揪出页面的JavaScript错误,看了下似乎是没有现成的,想想确实有必要做,列入计划却一直没做。直至有一次发布完冒烟却发现个历史遗留的JavaScript错误,想来很恐惧,这么多页面心里没底也不知道还遗留了多少,赶紧将此事提上日程把事儿给办了,免得后患无穷。
还是用的User Extension的办法,具体实施细节可以参考:Selenium User Extension。Selenium的User Extension很强大啊,可以自由扩展功能。
放些代码,与人方便:)
user-extensions.js文件,这里面调用了Firefox的XPCOM,获取控制台的JavaScript错误。下面介绍三个函数,后面会在client driver里调用这三个函数。这三个函数的作用分别如下:
1、doBeginJsErrorChecker : Registers a listener for notification when an error is logged。
2、getJSErrorsChecker : error会存在一个全局变量里,执行这个函数时返回所有的error。
3、doEndJsErrorChecker : Unregisters a listener。
// ==================================================
// Report Javascript Errors using Selenium Exceptions
// ==================================================
// Courtesy of Jerry Qian (http://sejq.blogspot.com/)
// http://clearspace.openqa.org/message/52135
// http://jira.openqa.org/browse/SEL-613
//
// Adapted to work outside of the Selenium IDE:
//
// https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIConsole...
// https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIScriptE...
// https://developer.mozilla.org/en/Setting_up_extension_development_env...
if (browserVersion.isChrome) {
var JSErrors = new Array();
var theConsoleListener = {
observe:function( aMessage ){//async!
if(aMessage instanceof Components.interfaces.nsIScriptError &&
aMessage.flags != Components.interfaces.nsIScriptError.warningFlag){
JSErrors.push(aMessage.message);
}
},
QueryInterface: function (iid) {
if (!iid.equals(Components.interfaces.nsIConsoleListener) &&
!iid.equals(Components.interfaces.nsISupports)) {
throw Components.results.NS_ERROR_NO_INTERFACE;
}
return this;
}
};
}
Selenium.prototype.getJSErrorsChecker = function(){
var errors = "";
if (browserVersion.isChrome) {
for (var i=0;i<JSErrors.length;i++)
errors += "Error: [" + JSErrors[i] + "];";
}
else {
throw new SeleniumError("TODO: Non-FF browser...");
}
return errors;
};
Selenium.prototype.doBeginJsErrorChecker = function(){
JSErrors = new Array();
try {
if (browserVersion.isChrome) {// firefox
var aConsoleService = Components.classes["@mozilla.org/consoleservice;1"]
.getService(Components.interfaces.nsIConsoleService);
aConsoleService.registerListener(theConsoleListener);
aConsoleService.reset();
}
else{
throw new SeleniumError("TODO: Non-FF browser...");
}
}
catch (e) {
throw new SeleniumError("Threw an exception: " + e.message);
}
};
Selenium.prototype.doEndJsErrorChecker = function(){
try {
if (browserVersion.isChrome) {// firefox
var aConsoleService = Components.classes["@mozilla.org/consoleservice;1"]
.getService(Components.interfaces.nsIConsoleService);
aConsoleService.unregisterListener(theConsoleListener);
aConsoleService.reset();
}
else{
throw new SeleniumError("TODO: Non-FF browser...");
}
}
catch (e) {
throw new SeleniumError("Threw an exception: " + e.message);
}
};
运行rc时,指定userExtensions文件。之前用selenium server 0.9.2版本的,在firefox里总是有个JavaScript错误:"setting a property that has only a getter",结果一堆误报。Google了一下发觉也有其他人遇到过,更新到最新版本的selenium-server-standalone-2.4.0.jar就好了。
java -jar yourdir\selenium-server-standalone-2.4.0.jar -userExtensions yourdir\user-extensions.js
client driver的代码,改写selenium源文件也是可以的,不过这样不方便维护,可以写一个类继承DefaultSelenium。需要注意的是在user-extensions.js中,函数的命名如果是以do开头的,比如doBeginJsErrorChecker,那么在调用时需要写成beginJsErrorChecker。如果是以get开头的,比如getJSErrorsChecker,那么调用的时候就直接getJSErrorsChecker。
Actions are named starting with “do”, while accessors are named starting with “get”.
You can see in selenium-commandhandlers.js that the two are registered differently (look for _registerAllAccessors compared to _registerAllActions).
public void beginJsErrorChecker() {
commandProcessor.doCommand("beginJsErrorChecker", new String[] {});
}
public String getJsError() {
return commandProcessor.doCommand("getJSErrorsChecker", new String[] {});
}
public void endJsErrorChecker() {
commandProcessor.doCommand("endJsErrorChecker", new String[] {});
}
具体的case中,在case开始的时候beginJsErrorChecker,case执行结束后getJsError获取控制台的JavaScript错误,并endJsErrorChecker。
@Test
public void test_jsErrorCheck() {
selenium.beginJsErrorChecker();
selenium.open("/");
//具体的操作
String jsError = selenium.getJsError();
assertTrue("JavaScript Error: "+jsError,"OK,".equals(jsError));
selenium.endJsErrorChecker();
}
可以想到每个case都要做这样的初始化操作,利用Junit的Setup和tearDown的话就行了(其实不必每个testcase都做初始化,只要把那个记录error的全局变量给清空就行,看自己需要吧)。我在这里做了一件傻事情,在endJsErrorChecker之前先执行assert,结果assert失败(也即是页面有Js错误)时,后面那句endJsErrorChecker没有执行,再次beginJsErrorChecker时就悲剧了,导致后面的case全出问题了。多亏师兄帮我找到原因n_n
public void setUp() {
selenium.beginJsErrorChecker();
}
public void tearDown() {
String jsError = selenium.getJsError();
selenium.endJsErrorChecker();
assertTrue("JavaScript Error: "+jsError,"OK,".equals(jsError));
}
运行了两百多个testcase,发现两个很隐蔽的bug,心里暗爽,今后终于可以放心优化JS代码了!









































