本周微信热更了新的 X5,切换不好使了。chromedriver 拿不到 session 了。具体见这个贴:https://testerhome.com/topics/8990
高手碰到过不? @seveniruby @chenhengjie123
是的,上午填了下坑。x5 会热更新。太悲催了。 目前看是 chromedriver 连不上 session
log:
Proxying [POST /session] to [POST http://127.0.0.1:8000/wd/hub/session] with body: {"desiredCapabilities":{"chromeOptions":{"androidPackage":"com.tencent.mm","androidUseRunningApp":true,"args":[],"extensions":[],"androidProcess":"com.tencent.mm:tools","androidDeviceSerial":"e858310c"}}}
[debug] [JSONWP Proxy] Got response with status 200: {"sessionId":"820b6785ffe0d35cb300b63ac7415042","status":33,"value":{"message":"session not created exception: please close '' and try again\n (Driver info: chromedriver=2.26.436421 (6c1a3ab469ad86fd49c8d97ede4a6b96a49ca5f6),platform=Mac OS X 10.12.0 x86_64)"}}
手工启动 chrome driver
curl -X POST \
http://127.0.0.1:8000/wd/hub/session \
-H 'cache-control: no-cache' \
-H 'content-type: application/json' \
-H 'postman-token: a1129cab-e998-ec36-7e60-541186147e0a' \
-d '{
"desiredCapabilities": {
"chromeOptions": {
"androidPackage": "com.tencent.mm",
"androidUseRunningApp": true,
"androidProcess": "com.tencent.mm.plugin.webview.ui.tools.WebViewUI",
"androidDeviceSerial": "e858310c"
}
}
}'
得到的返回是这玩意儿:
"sessionId": "6ece983a80331d68d5b80e87b8e78e48",
"status": 33,
"value": {
"message": "session not created exception: please close '' and try again\n (Driver info: chromedriver=2.26.436421 (6c1a3ab469ad86fd49c8d97ede4a6b96a49ca5f6),platform=Mac OS X 10.12.0 x86_64)"
}
}
Chrome driver 的说明:
androidDeviceSerial: (Optional) The device serial number on which to launch the app (See Multiple Devices section below).
androidUseRunningApp: (Optional) Attach to an already-running app instead of launching the app with a clear data directory.
The following capabilities are only applicable to WebView apps.
androidActivity: Name of the Activity hosting the WebView.
androidProcess: (Optional) Process name of the Activity hosting the WebView (as given by ps). If not given, the process name is assumed to be the same as androidPackage.
手工打开 chromediver,并打日志的方法: 加上--verbose 就会把日志输出到控制台了。
/usr/local/lib/node_modules/appium/node_modules/appium-chromedriver/chromedriver/mac/chromedriver --url-base=wd/hub --port=8000 --adb-port=5037 --verbose
如果单独启动我的日志会报这种错误
[6.839][DEBUG]: DevTools request: http://localhost:12088/json/version
[6.841][DEBUG]: DevTools request failed
[6.892][DEBUG]: DevTools request: http://localhost:12088/json/version
[6.894][DEBUG]: DevTools request failed
[6.944][DEBUG]: DevTools request: http://localhost:12088/json/version
[6.945][DEBUG]: DevTools request failed
又一个坑,这个玩得太少,得找个老鸟来搞啊。呼唤大神。
遇上了同样的问题。
不同版本,不同 id 登录好像都有细微差别。猜测能热更新,并有各种灰度。坑不小。
1.插上真机就变成真机了,我是 appium 小白,机理不知道,猜测有个优先调用顺序。我微信在 genymode 里安装失败了,别的没试过。
2.只有微信历史列表是 h5 写的,并且需要操作它的元素,所以才切换,每次循环都切回来。我那个版本的代码有瑕疵,现在为了搞定各种异常,代码量已经翻了三四倍了,凑合着当例子看吧。
3.微信装在不同 android 机的同一个按钮竟然叫法不同。也是醉了。所以就算是要得到一个返回按钮的 id,要这么写:
public String PickID(AndroidDriver driver){
if(isElementExist(driver, By.id("h4")))
return "h4";
if(isElementExist(driver, By.id("gw")))
return "gw";
if(isElementExist(driver, By.id("go")))
return "go";
if(isElementExist(driver, By.id("gg")))
return "gg";
return null;
}
4.写前端自动化的代码是非常不愉快的经历。
5.仍旧觉得前端自动化如不是实在绕不过去,别花太多精力。投入产出比太低了。
感谢黄老师和各位提供细节的同学,全搞定了。作为 appium 的新手菜鸟回馈一下几个细节:
1.chromedriver 可以在起 appium 的时候当启动参数参数,这样就不用单起了 appium --no-reset --chromedriver-port 8000
2.capabilities.setCapability("recreateChromeDriverSessions", "True"); 这句很重要,否则 webview 和 native 来回切会有问题。
3.在微信里打开调试选项后,可以用 chrome 的内建插件 inspect H5 的文档结构。
贴下比较 low 的代码,代码实现了根据给定列表遍历给定公众号历史文章的 demo 功能,期间数次 native 和 webview 的切换:
import io.appium.java_client.android.AndroidDriver;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.remote.DesiredCapabilities;
import java.net.URL;
import java.util.ArrayList;
import org.junit.*;
/**
* Created by lucas on 2017/5/31.
*/
public class AndroidTest {
private static final int NUMBER_ALLOWED = 3;
@Test
public void invokeSnowball() throws Exception {
AndroidDriver driver = this.GetAndroidDriver();
//进入公众号列表
this.GotoList(driver);
//遍历列表
for (String name : this.GetSearchList()) {
if (this.GotoMessageHistory(driver, name)) {
//遍历列表给出的文章
this.getArticleDetail(driver, name);
}
//返回公众号列表
this.GoBackToList(driver);
}
}
//进入公众号列表的操作
private void GotoList(AndroidDriver driver) throws Exception {
Thread.sleep(5000);
driver.findElement(By.xpath("//android.widget.TextView[@text='通讯录']")).click();
Thread.sleep(2000);
driver.findElement(By.xpath("//android.widget.TextView[@text='公众号']")).click();
}
//定义搜索列表
private ArrayList<String> GetSearchList() {
return new ArrayList<String>() {{
add("漫画项目管理");
add("阿尔法工场");
add("阿里研究院");
add("安卓开发精选");
}};
}
private void getArticleDetail(AndroidDriver driver, String name) throws Exception {
for (int i = 1; i <= NUMBER_ALLOWED; i++) {
driver.context("WEBVIEW_com.tencent.mm:tools");
System.out.println("切换到WEBVIEW上下文");
if (isElementExist(driver, By.xpath("//*[@id='js_msg_card']"))) {
System.out.println("获取到了公众号文章列表");
//点击文章
if (isElementExist(driver, By.xpath("//*[@class='weui_msg_card_bd']/div[" + i + "]"))) {
driver.findElement(By.xpath("//*[@class='weui_msg_card_bd']/div[" + i + "]")).click();
Thread.sleep(2000);
System.out.println("公众号:" + name + "第" + i + "篇文章被点击");
//切换回native的context
driver.context("NATIVE_APP");
System.out.println("NATIVE_APP");
if (isElementExist(driver, By.id("h4"))) {
driver.findElement(By.id("h4")).click(); //返回对应公众号的文章列表.
System.out.println("关闭公众号文章,回到H5列表");
} else {
System.out.println("未能正常回退,请关注");
}
}
} else {
System.out.println("公众号没有历史文章");
}
}
//最后回到公众号列表,需要后退,切回native方式.
driver.context("NATIVE_APP");
System.out.println("NATIVE_APP");
}
//搜索一个公众号,并进入历史文章列表
private boolean GotoMessageHistory(AndroidDriver driver, String name) throws Exception {
Thread.sleep(2000);
driver.findElement(By.xpath("//android.widget.TextView[@content-desc='搜索']")).click();
Thread.sleep(2000);
WebElement inputGZ = driver.findElement(By.xpath("//android.widget.EditText[@text='搜索']"));
inputGZ.click();
inputGZ.sendKeys(name);
Thread.sleep(2000);
//搜到了的处理方式
if (isElementExist(driver, By.xpath("//android.widget.TextView[@text='" + name + "']"))) {
driver.findElement(By.xpath("//android.widget.TextView[@text='" + name + "']")).click();
Thread.sleep(2000);
Thread.sleep(2000);
driver.findElement(By.xpath("//android.widget.TextView[@content-desc='聊天信息']")).click();
Thread.sleep(2000);
driver.findElement(By.xpath("//android.widget.TextView[@text='查看历史消息']")).click();
Thread.sleep(5000); //页面加载时间太长
return true;
} else {
System.out.println("未能找到公众号:"+name);
return false;
}
}
private AndroidDriver GetAndroidDriver() throws Exception {
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("platformVersion", "7.0");
capabilities.setCapability("deviceName", "Android Emulator");
capabilities.setCapability("platformName", "Android");
capabilities.setCapability("appPackage", "com.tencent.mm");
capabilities.setCapability("appActivity", ".ui.LauncherUI");
capabilities.setCapability("unicodeKeyboard", "True");
capabilities.setCapability("resetKeyboard", "True");
capabilities.setCapability("recreateChromeDriverSessions", "True");
//加入微信webwiew调试能力.
ChromeOptions options2 = new ChromeOptions();
options2.setExperimentalOption("androidProcess", "com.tencent.mm:tools");
capabilities.setCapability(ChromeOptions.CAPABILITY, options2);
return new AndroidDriver<WebElement>(new URL("http://127.0.0.1:4723/wd/hub"), capabilities);
}
//回到list
private void GoBackToList(AndroidDriver driver) throws Exception {
while (isElementExist(driver, By.id("h4"))) {
driver.findElement(By.id("h4")).click(); //碰上h4就不停返回
Thread.sleep(2000);
}
if (isElementExist(driver, By.id("go"))) {
driver.findElement(By.id("go")).click(); //再多试一遍
Thread.sleep(2000);
}
if (isElementExist(driver, By.id("gw"))) {
driver.findElement(By.id("gw")).click(); //再多试一遍
Thread.sleep(2000);
}
System.out.println("已经回到搜索列表");
}
private boolean isElementExist(AndroidDriver driver, By locator) {
try {
driver.findElement(locator);
return true;
} catch (NoSuchElementException ex) {
return false;
}
}
}
问题解决了。可以来回切换了。感谢!
我也碰到了类似问题。杀 chromedriver 进程有点儿郁闷。各位大神还有别的好办法么? @Lihuazhang @seveniruby
不切 driver 可以么?
开始以为 android 的底层 driver 不支持。好像理解错了。
ios 下。android 有戏么?
搞啊。
哈哈,好问题。
写成 sdk,能用。
是的。不及格。
测试是一个完整的活动过程,测试设计和测试执行不应该割裂来。并且执行的时候决不能就照着用例来,不动脑子。如果不动脑子的话,写好测试用例发现 bug 的比率不如 free test 的五分之一(原来做过一个上千用例级别的试验)。
测试用例的主要作用应该是:辅助人脑做备忘,启发测试人员,而不应该是操作手册。 强推一本书,看看这个作者是怎么说的,能够很好的回答你的问题。 http://www.thetesteye.com/papers/TheLittleBlackBookOnTestDesign.pdf
POST 可以拿到,得做点儿处理。 URLConnection 和 URLSession 兼容是个问题。现在想到的方法是动态识别适配,要有一大坨代码写。
迅速
Appium 还是超时。直接 localhost 直接 curl 那个接口很不稳定,经常没有返回。iproxy 的日志:
recv failed: Resource temporarily unavailable
recv failed: Resource temporarily unavailable
recv failed: Resource temporarily unavailable
recv failed: Resource temporarily unavailable
recv failed: Resource temporarily unavailable
国行真机真的有点儿扯。
稍后试试 WDA 自己的 USB 链接方式看看行不行。
也可以试试这个:https://bitbucket.org/csolar/jat 代码会清爽一些:)
Best wishes 兄弟。你很棒,做好你自己,继续保持大师风范,酒是陈年香。
黄老师新作,拜读啦:)大大的赞。
已过。感觉还能干好久。
愿社区越办越好。