Appium 微信 H5 等 H5 应用自动化测试如何与 PC 端 WebUI 自动化统一入口

土豆 · March 11, 2017 · Last by nmvjhd replied at October 09, 2018 · 6660 hits

时下H5应用很火,微信公众号,微信小程序,支付宝服务窗等,其应用跟PC端的Web应用有共同也有区别之处,但其实H5应用跟pc端的web应用是差不多的,其本质都是web应用,都是要通过浏览器或者Webview浏览器进行操作,只不过H5应用更多的是运行在手机端而不是PC端。由于设备端不一样,H5应用是在手机端,没法像PC端的WEB应用那么简单,所以很多时候,可能是把H5划归到APP自动化进行管理的。既然有这么多共同之处,那么有没有办法把它封装成一种浏览器,归类到Web自动化测试中呢,达到跟WEB UI统一的操作入口?看了思寒的微信Webview自动化测试方法的帖子,给了我灵感,特分享出来

网上流传的H5自动化测试有两种方案,一种是利用PC端谷歌浏览器的设备UA模式,模拟手机浏览器,一种是利用Appium连接手机,进入应用切换到Webview;一种方案是比较简单的,但是没有办法完全模拟手机浏览器的环境,所以采用第二种方法。下面说下具体的过程
一、将apium注册到selenium gird
采用Appium 的方式,Appium Driver启动方式与Webdriver的启动方式略有不同,那么如何达到统一的入口呢?Selenium有一种分布式解决方案Selenium Grid,如果把Appium Server 注册到Selenium Grid hub上就实现了统一的入口了。appium 注册到selenium grid的方法,网上有很多例子,但经过实验发现,有几个地方需要特别注意:
1、注册node节点的时候需指定udid, 2、启动的时候要顺带启动appium 内置的chromedriver,3、appium 地址,grid hub地址不能指定为127.0.0.1,不然其他机器就没办法通过ip进行访问了。下面是我的appium 注册代码:
nodeconfig如下:

{  
"capabilities": [
{
"browserName": "chrome",
"version": "5.0.1",
"maxInstances": 1,
"platform": "ANDROID",
"udid":"127.0.0.1:62001",
"deviceName":"127.0.0.1:62001"
}
],
"configuration": {
"cleanUpCycle": 2000,
"timeout":30000,
"proxy": "org.openqa.grid.selenium.proxy.DefaultRemoteProxy",
"hub":"192.168.1.103:4455/grid/register",
"url":"http://192.168.1.103:4723/wd/hub",
"host": "192.168.1.103",
"port": 4723,
"maxSession": 1,
"register": true,
"registerCycle": 5000,
"hubPort": 4455,
"hubHost": "192.168.1.103"
}
}

appium 启动脚本:

appium --address 192.168.1.103 --port 4723  --bootstrap-port 4724 --chromedriver-port 8000  --session-override --nodeconfig D:\auto\appium-node\appium-node.json

二、设计testbase(浏览器启动和关闭)类

原理:由于微信浏览器没有像PC浏览器一样有网址输入口,所以需要通过文件传输助手输入自己设计的网址入口地址(类似hao123入口)

public class TestBaseCase {
public static WebDriver driver;
public static String description;
public Log log=new Log(this.getClass().getSuperclass());
public void setup( String driver,String nodeURL) throws MalformedURLException {
log.info("------------------开始执行测试---------------");
log.info("读取xml配置:浏览器:"+driver+";gridNodeURL:"+nodeURL);
try {
this.driver=setRemoteDriver(driver,nodeURL);
} catch (Exception e) {
log.error("没有成功浏览器环境配置错误");
}

this.driver.manage().window().maximize();

}

@AfterTest
public void tearDown() {
try {
this.driver.close();
this.driver.quit();
}catch (Exception e) {
log.info("android driver 退出需切换会NATIVE_APP");
AndroidDriver androidDriver=(AndroidDriver) driver;
androidDriver.quit();
}
}
private WebDriver setRemoteDriver(String browsername,String nodeURL) throws MalformedURLException
{
switch (browsername)
{

case "FirefoxDriver" :
DesiredCapabilities capabilities=DesiredCapabilities.firefox();
capabilities.setBrowserName("firefox");
capabilities.setPlatform(Platform.WINDOWS);
driver= new RemoteWebDriver(new URL(nodeURL), capabilities);
break;
case "ChormeDriver":
DesiredCapabilities dcchorme=DesiredCapabilities.chrome();
dcchorme.setBrowserName("chrome");
dcchorme.setVersion("46.0.2490.86 m");
dcchorme.setPlatform(Platform.WINDOWS);
driver=new RemoteWebDriver(new URL(nodeURL), dcchorme);
break;
case "WeiXIN":
DesiredCapabilities capability = new DesiredCapabilities();
capability.setCapability("app", "");
capability.setCapability("appPackage", "com.tencent.mm");
capability.setCapability("appActivity", ".ui.LauncherUI");
capability.setCapability("deviceName", "127.0.0.1:62001");
capability.setCapability("fastReset", "false");
capability.setCapability("fullReset", "false");
capability.setCapability("noReset", "true");
capability.setCapability("unicodeKeyboard", "True");
capability.setCapability("resetKeyboard", "True");
//关键是加上这段
ChromeOptions options2 = new ChromeOptions();
options2.setExperimentalOption("androidProcess", "com.tencent.mm:tools");
capability.setCapability(ChromeOptions.CAPABILITY, options2);
//启动微信浏览器
log.info("启动微信浏览器");
driver= new AndroidDriver(new URL(nodeURL), capability);
driver.manage().timeouts().implicitlyWait(500, TimeUnit.MILLISECONDS);
sleep(5);
log.info("点击微信搜索菜单");
WebElement webElement=driver.findElement(By.xpath("//*[@content-desc='搜索']"));
webElement.click();
log.info("输入文件字符串");
webElement=driver.findElement(By.xpath("//*[@text='搜索']"));
webElement.click();
webElement.clear();
webElement.sendKeys("文件");
sleep(4);
log.info("点击文件传输助手");
webElement=driver.findElement(By.xpath("//*[@text='文件传输助手']"));
webElement.click();
sleep(8);
log.info("发送跳转网站的网页URL");
webElement= driver.findElement(By.xpath("//*[@resource-id='com.tencent.mm:id/z4']"));//不同微信版本,定位不一样
webElement.sendKeys("http://192.168.1.103:8080/openurl/open.html");
log.info("点击发送按钮");
webElement=driver.findElement(By.xpath("//*[@text='发送']"));
webElement.click();
sleep(3);
log.info("点击网址");
webElement=driver.findElement(By.xpath("//*[@text='"+"http://192.168.1.103:8080/openurl/open.html"+"']"));
webElement.click();
sleep(3);
log.info("切换到微信webView");
//Webdriver转AndroidDriver
AndroidDriver androidDriver=(AndroidDriver) driver;
androidDriver.context("WEBVIEW_com.tencent.mm:tools");
driver=androidDriver;

case "HtmlUnitDriver":
this.driver=new HtmlUnitDriver();
break;
default:
this.driver=new FirefoxDriver();
break;
}
return driver;
}
}

三、设计网址入口网页


<html>
<head>
<meta charset="GBK"/>
<script src="http://cdn.static.runoob.com/libs/jquery/1.10.2/jquery.min.js">
</script>
</head>
<body style="text-align:center">
<div>
<input id="url" type="text"/>
<input id="openurl" type="button" value="打开网页"/>
</div>
<script>
$("#openurl").click(function(){
var url=$("#url").val();
location.href=url;
}
)
</script>
</body>
</html>

四、设计open方法

//微信浏览器操作
public void openWeiXinBrowser(String url) {
WebElement webElement=driver.findElement(By.id("url"));
log.info("输入网址:"+url);
webElement.sendKeys(url);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
webElement=driver.findElement(By.id("openurl"));
log.info("点击打开网页");
webElement.click();
}

五、设计截图方法

public class ScreenShot {
public WebDriver driver;
private String screenName;
Log log =new Log(this.getClass());
public void setscreenName(String screenName)
{
this.screenName=screenName;
}
public ScreenShot(WebDriver driver)
{
this.driver=driver;
}
private void takeScreenshot(String screenPath) {
try{
scrFile=((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
}catch (Exception e)//关键就在于此
{
//appium 内chrome没法截图,需用原生app下进行截图
System.out.println("进入到了webview 截图需切换回 NATIVE_APP content");
AndroidDriver driver2=((AndroidDriver) driver);//关键就在于此
log.info("contextName:"+driver2.getContext());
System.out.println(driver2.getContextHandles());
// log.info(driver2.getContextHandles().toString());
log.info("切换到NATIVE_APP进行app截图");
driver2.context("NATIVE_APP");
scrFile=((TakesScreenshot) driver2).getScreenshotAs(OutputType.FILE);
log.info("切换回微信webviw");
driver2.context("WEBVIEW_com.tencent.mm:tools");
}
try {
Files.copy(scrFile, new File(screenPath));
log.error("错误截图:"+screenPath);
} catch (Exception e) {
e.printStackTrace();
}
}

public void takeScreenshot() {
String screenName =this.screenName+ ".jpg";
File dir = new File("test-output\\snapshot");
if (!dir.exists())
{dir.mkdirs();}
String screenPath = dir.getAbsolutePath() + "\\" + screenName;
this.takeScreenshot(screenPath);
}

}

到此就完成了微信浏览器入口统一,这里面的坑是AndroidDriver的Contentext方法并没有在Webdriver里定义,而微信截图等很多地方需要切换Contentext才能操作,而要达到入口统一肯定需要通过Webdriver接口作为浏览器的管理;这个坑在网上也没有答案,冥思苦想后终于想出了类型转换的方法,我也是佩服自己能想出接口与实现类之间类型转换的方法。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 7 条回复 时间 点赞

能否留个联系方式,请教你一下问题,或者加我微信也行Chenjun821710

哎呀,树恒,截图实在不行就用adb实现了

不切driver可以么?

楼主我用你微信那块代码运行报如下错,不知道什么原因。chromedriver版本换了 chromedriver=2.26, chromedriver=2.33,chromedriver=2.38都不行

[INFO ] 2018-04-19 11:48:52,751 method:com.jnt.csair.weixin.H5Weixin.main(H5Weixin.java:41)
启动微信浏览器
Exception in thread "main" org.openqa.selenium.WebDriverException: An unknown server-side error occurred while processing the command. Original error: Failed to start Chromedriver session: An unknown server-side error occurred while processing the command. (Original error: unknown error: known package com.android.chrome does not accept activity/process
(Driver info: chromedriver=2.26.436362 (5476ec6bf7ccbada1734a0cdec7d570bb042aa30),platform=Windows NT 10.0.15063 x86_64)) (WARNING: The server did not provide any stacktrace information)
Command duration or timeout: 23.01 seconds
Build info: version: '3.11.0', revision: 'e59cfb3', time: '2018-03-11T20:33:15.31Z'
System info: host: 'JIANGNENGTING', ip: '192.168.18.254', os.name: 'Windows 10', os.arch: 'amd64', os.version: '10.0', java.version: '1.8.0_131'
Driver info: driver.version: AndroidDriver
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Unknown Source)
at org.openqa.selenium.remote.ErrorHandler.createThrowable(ErrorHandler.java:214)
at org.openqa.selenium.remote.ErrorHandler.throwIfResponseFailed(ErrorHandler.java:166)
at org.openqa.selenium.remote.JsonWireProtocolResponse.lambda$new$0(JsonWireProtocolResponse.java:53)
at org.openqa.selenium.remote.JsonWireProtocolResponse.lambda$getResponseFunction$2(JsonWireProtocolResponse.java:91)
at org.openqa.selenium.remote.ProtocolHandshake.lambda$createSession$0(ProtocolHandshake.java:123)
at java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)
at java.util.Spliterators$ArraySpliterator.tryAdvance(Unknown Source)
at java.util.stream.ReferencePipeline.forEachWithCancel(Unknown Source)
at java.util.stream.AbstractPipeline.copyIntoWithCancel(Unknown Source)
at java.util.stream.AbstractPipeline.copyInto(Unknown Source)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)
at java.util.stream.FindOps$FindOp.evaluateSequential(Unknown Source)
at java.util.stream.AbstractPipeline.evaluate(Unknown Source)
at java.util.stream.ReferencePipeline.findFirst(Unknown Source)
at org.openqa.selenium.remote.ProtocolHandshake.createSession(ProtocolHandshake.java:126)
at org.openqa.selenium.remote.ProtocolHandshake.createSession(ProtocolHandshake.java:73)
at org.openqa.selenium.remote.HttpCommandExecutor.execute(HttpCommandExecutor.java:136)
at io.appium.java_client.remote.AppiumCommandExecutor.execute(AppiumCommandExecutor.java:129)
at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:545)
at io.appium.java_client.DefaultGenericMobileDriver.execute(DefaultGenericMobileDriver.java:42)
at io.appium.java_client.AppiumDriver.execute(AppiumDriver.java:1)
at io.appium.java_client.android.AndroidDriver.execute(AndroidDriver.java:1)
at org.openqa.selenium.remote.RemoteWebDriver.startSession(RemoteWebDriver.java:209)
at org.openqa.selenium.remote.RemoteWebDriver.(RemoteWebDriver.java:132)
at io.appium.java_client.DefaultGenericMobileDriver.(DefaultGenericMobileDriver.java:38)
at io.appium.java_client.AppiumDriver.(AppiumDriver.java:84)
at io.appium.java_client.AppiumDriver.(AppiumDriver.java:94)
at io.appium.java_client.android.AndroidDriver.(AndroidDriver.java:79)
at com.jnt.csair.weixin.H5Weixin.main(H5Weixin.java:42)

原码如下: 有知道的兄弟们不.

import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.remote.DesiredCapabilities;

import io.appium.java_client.android.AndroidDriver;

public class H5Weixin {

private static Logger logger = Logger.getLogger(H5Weixin.class);

public static void main(String[] args) throws InterruptedException, MalformedURLException {
// TODO Auto-generated method stub
AndroidDriver driver = null;
DesiredCapabilities capability = null;

capability = new DesiredCapabilities();
capability.setCapability("app", "");
capability.setCapability("appPackage", "com.tencent.mm");
capability.setCapability("appActivity", ".ui.LauncherUI");
capability.setCapability("deviceName", "3bf6f87e");
capability.setCapability("fastReset", "false");
capability.setCapability("fullReset", "false");
capability.setCapability("noReset", "true");
// capability.setCapability("unicodeKeyboard", "True");
// capability.setCapability("resetKeyboard", "True");

// 关键是加上这段
ChromeOptions options2 = new ChromeOptions();
options2.setExperimentalOption("androidProcess", "com.tencent.mm:tools");
capability.setCapability(ChromeOptions.CAPABILITY, options2);

// 启动微信浏览器
logger.info("启动微信浏览器");

driver = new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"), capability);

// driver.manage().timeouts().implicitlyWait(500, TimeUnit.MILLISECONDS);
// Thread.sleep(5000);
//
// logger.info("点击微信搜索菜单");
// WebElement webElement = driver.findElement(By.xpath("//[@content-desc='搜索']"));
// webElement.click();
// logger.info("输入文件字符串");
// webElement = driver.findElement(By.xpath("//
[@text='搜索']"));
// webElement.click();
// webElement.clear();
// webElement.sendKeys("文件");
// Thread.sleep(5000);
// logger.info("点击文件传输助手");
// webElement = driver.findElement(By.xpath("//*[@text='文件传输助手']"));
// webElement.click();
// Thread.sleep(5000);
// logger.info("发送跳转网站的网页URL");

// webElement = driver.findElement(By.xpath("//[@resource-id='com.tencent.mm:id/z4']"));// 不同微信版本,定位不一样
// webElement.sendKeys("http://192.168.1.103:8080/openurl/open.html");
// logger.info("点击发送按钮");
// webElement = driver.findElement(By.xpath("//
[@text='发送']"));
// webElement.click();
// Thread.sleep(5000);
// logger.info("点击网址");
// webElement = driver.findElement(By.xpath("//*[@text='" + "http://192.168.1.103:8080/openurl/open.html" + "']"));
// webElement.click();
// Thread.sleep(5000);
// logger.info("切换到微信webView");

// Webdriver转AndroidDriver
AndroidDriver androidDriver = (AndroidDriver) driver;
androidDriver.context("WEBVIEW_com.tencent.mm:tools");
driver = androidDriver;

}

}

@jntdream ,请问你的问题解决了么,怎么解决的,能不能share下啊?
我遇到和你一样的问题了:(

有遇到切换webview的context时报chrome not reachable的吗,卡在这里好几天了!

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up