• 微信 webview 的自动化技术 at 2017年06月15日

    本周微信热更了新的 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

    又一个坑,这个玩得太少,得找个老鸟来搞啊。呼唤大神。

  • 遇上了同样的问题。

  • 微信 webview 的自动化技术 at 2017年06月15日

    不同版本,不同 id 登录好像都有细微差别。猜测能热更新,并有各种灰度。坑不小。

  • 微信 webview 的自动化技术 at 2017年06月15日

    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.仍旧觉得前端自动化如不是实在绕不过去,别花太多精力。投入产出比太低了。

  • 微信 webview 的自动化技术 at 2017年06月07日

    感谢黄老师和各位提供细节的同学,全搞定了。作为 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;
            }
        }
    
    
    }
    
  • 微信 webview 的自动化技术 at 2017年06月07日

    问题解决了。可以来回切换了。感谢!

  • 微信 webview 的自动化技术 at 2017年06月06日

    我也碰到了类似问题。杀 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 兄弟。你很棒,做好你自己,继续保持大师风范,酒是陈年香。

  • [思寒] 测试职业发展简谈 at 2017年01月04日

    黄老师新作,拜读啦:)大大的赞。

  • 已过。感觉还能干好久。

  • 坚持的力量 at 2016年12月02日

    愿社区越办越好。