Athrun框架WebView自动化测试源码解析
虽然Athrun对WebView的支持不是很完美,但一些基本功能还是可以完成的,比如:查找一个名字为“HelloWeb”的Web Element,并将其值改为“Hello World!”。相应的用法如下:
webview.typeTextInWebElement(By.name("HelloWeb"), "Hello World!", 0);
一 WebView自动化测试的应用与原理
1 进入Web页面
首先,它的执行步骤和Native View的操作步骤是一致的,通过形如以下的代码进入真正的Web页面:
findElementByText("WebView").doClick();
WebViewElement webview = findWebElementById("mywebview", WebViewElement.class);
2 查找Web元素
接下来,我们需要在WebView页面上找到相应的子元素。Athrun框架中封装了一个By类,并扩展了多个内部子类用于封装各种Web子控件的查找方法,如:Id ,Xpath ,CssSelector,ClassName,Name,Text,TagName等。请看以下例子:
WebElement webEle = webview.getWebElement(By.className("btn-bg"),0);
上述例子的具体含义为:通过ClassName为“btn-bg”来查找WebElement。
那为什么通过这个ClassName就能找到呢?这个问题是最关键。请看下面的调用顺序:
getWebElement 调 用
waitForWebElement调 用
searchForWebElement 调 用
getCurrentWebElements,getViewFromList
其实直接返回WebElement的是searchForWebElement方法,我们看看它的具体实现:
/**
* Searches for a web element.
*
* @param by the By object.
* @param minimumNumberOfMatches the minimum number of matches that are expected to be shown. 0 means any number of matches
* @param timeout the amount of time in milliseconds to wait
* @param scroll true if scrolling should be performed
* @return true if the web element is found
*/
private WebElement searchForWebElement(final By by, int minimumNumberOfMatches, int timeout, boolean scroll){
if(minimumNumberOfMatches < 1){
minimumNumberOfMatches = 1;
}
List<WebElement> viewsFromScreen = webUtils.getCurrentWebElements(by);
addViewsToList (webElements, viewsFromScreen);
return getViewFromList(webElements, minimumNumberOfMatches);
}
在searchForWebElement中,先调用getCurrentWebElements(by)获取所有条件符合by的WebElements,然后通过getViewFromList获取webElements列表中符合参数minimumNumberOfMatches的WebElement。
getViewFromList挺好理解的,关键是getCurrentWebElements这个方法。它是怎么做到的呢?
/**
* Returns an ArrayList of WebElements of the specified By object currently shown in the active WebView.
*
* @param by the By object.
* @return an ArrayList of the WebElement objects currently shown in the active WebView
*/
public ArrayList<WebElement> getCurrentWebElements(final By by) {
boolean javaScriptWasExecuted = executeJavaScript(by);
return getSufficientlyShownWebElements(javaScriptWasExecuted);
}
上文中的executeJavaScript和getSufficientlyShownWebElements才是解决问题的突破口。
executeJavaScript:在java中执行js脚本
getSufficientlyShownWebElements:保存所有完全显示在界面上的web元素。
executeJavaScript方法做了哪些工作呢?
I 通过读取Athrun框架下的WebView.js(WebUtils-> getJavaScriptAsString)文件,并将读取的内容保存在一个String中。而WebView.js中定义了各种查找页面元素的方法,如:
function id(id)、
function xpath(xpath)、
function cssSelector(cssSelector)等。
II通过By的具体类型(Id,Name,CssSelector等),拼凑出一个function的调用,其中包括function名及其参数,并以String的类型返回,如:
executeJavaScriptFunction("id(\"" + by.getValue() + "\");")中的
"id(\"" + by.getValue() + "\");"
III 在方法executeJavaScriptFunction方法中,将I和II返回的结果拼凑,并加上前缀“javascript:”,最终就变成:
Javascript:+ WebView.js原文+ id(by.getValue());并将其传给webView.loadUrl方法,如:
webView.loadUrl("javascript:" + javaScript + function);
因为id(String)方法在WebView.js中已声明定义了,所以是合法调用。
通过执行executeJavaScript方法,找到了符合by的所有web元素,接下来就调用getSufficientlyShownWebElements方法将所有显示在页面上的元素保存在一个数据中,也就的到了getCurrentWebElements所要的结果了,所以想要的元素也找到了,所以就可以触发点击事件,输入文本事件了。
3 触发动作
获取到WebElement后,就可以获取其Location,如:
webElement.getLocationX()
webElement.getLocationY()
然后就可以clickOnScreen了,click其实也是通过Instrumentation发送event事件的。如下所示:
/**
* Clicks on a given coordinate on the screen.
*
* @param x
* the x coordinate
* @param y
* the y coordinate
*/
public void clickOnScreen(float x, float y) {
long downTime = SystemClock.uptimeMillis();
long eventTime = SystemClock.uptimeMillis();
MotionEvent event = MotionEvent.obtain(downTime, eventTime,
MotionEvent.ACTION_DOWN, x, y, 0);
MotionEvent event2 = MotionEvent.obtain(downTime, eventTime,
MotionEvent.ACTION_UP, x, y, 0);
try{
inst.sendPointerSync(event);
inst.sendPointerSync(event2);
}catch(SecurityException e){
Assert.assertTrue("Click can not be completed!", false);
}
}
现在整个WebView的自动化测试就完成了。
总结:WebView自动化测试,其实就是Java执行Js的一系列操作。