1、微信小程序自动化
由于腾讯系QQ、微信是基于腾讯自研X5内核-类似webview,不是谷歌原生webview,所以调试会有些
许差异(有很多app厂商也开始采用X5内核)
微信小程序自动化测试只能够支持手机,模拟器是不行的
- step1:打开微信小程序webview调试开关
聊天窗口输入如下URL,然后点击即打开调试开关:
http://debugmm.qq.com/?forcex5=true
http://debugx5.qq.com
- step2:UC开发者工具识别小程序的web元素信息
- step3:确认微信小程序对应的进程名 --com.tencent.mm:appbrand0
微信有很多的进程,我们要确定当前小程序是位于哪个进程中:
adb shell dumpsys activity top | findstr ACTIVITY 查询前台运行的所有进程
- step4:X5内核启动参数配置
// 支持X5内核应用自动化配置 desiredCapabilities.setCapability("recreateChromeDriverSessions", true); // ChromeOptions使用来定制启动选项,因为在appium中切换context识别webview的时候, // 由于小程序是在一个单独的进程中,所以需要加上androidProcess: com.tencent.mm:appbrand0 ChromeOptions options = new ChromeOptions(); // 第二个参数要改成你查询到的小程序进程名 options.setExperimentalOption("androidProcess", "com.tencent.mm:appbrand0"); desiredCapabilities.setCapability(ChromeOptions.CAPABILITY, options); // 初始化会默认将chrome浏览器打开,需要将Browser置为空 desiredCapabilities.setBrowserName("");
- step5:一定记得加!!!不清除微信的数据
//不清除掉微信的数据 capabilities.setCapability("noReset",true);
- step6:编写脚本
//微信启动需要时间,所以这里需要等待 Thread.sleep(15000); //下拉,滑动,展示出来小程序列表 swipeDown(); androidDriver.findElementByAndroidUIAutomator("new UiSelector().text(\"柠檬班软件… \")").click(); //小程序加载需要时间,再等待 Thread.sleep(15000); //切换到小程序对应的context中 androidDriver.context("WEBVIEW_com.tencent.mm:appbrand0"); //获取到所有的窗口 -- 句柄 Set<String> handles = androidDriver.getWindowHandles(); //对所有的窗口进行遍历,满足条件: 只要这个窗口包含特定的:URL/标题 for (String handle : handles) { //否则我们就去切换handle句柄 窗口 androidDriver.switchTo().window(handle); if(androidDriver.getTitle().equals("腾讯课堂柠檬班软件测试")){ //说明当前我们确实是在对应的窗口中 break; } } //点击课程 androidDriver.findElement(By.xpath("//a[contains(text(), \'课程\')]")).click();
2、微信小程序测试强调几点
- 真机测试
- 两个url点击,开启开关
- 进程名自己输入查看
- 页面下滑,选中名字,名字有可能缩写,按工具的缩写写
- 切换context到webview,另外小程序会打开三个页面,所以要遍历,进行窗口切换确认,什么时候用app的元素定位,什么时候用网页的元素定位,参数别搞错
- 微信小程序兼容的chromeDriver是V2.40,不按给出的版本选择
import com.lemon.data.Constants; import io.appium.java_client.MobileBy; import io.appium.java_client.TouchAction; import io.appium.java_client.android.AndroidDriver; import io.appium.java_client.touch.WaitOptions; import io.appium.java_client.touch.offset.PointOption; import org.openqa.selenium.By; import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.remote.DesiredCapabilities; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import java.net.MalformedURLException; import java.net.URL; import java.sql.Time; import java.time.Duration; import java.util.Set; import java.util.concurrent.TimeUnit; public class SmallProgramTest { public AndroidDriver androidDriver; @BeforeTest public void setUp() throws MalformedURLException { //启动微信 DesiredCapabilities desiredCapabilities = new DesiredCapabilities(); desiredCapabilities.setCapability("deviceName", "08e7c5997d2a"); desiredCapabilities.setCapability("platformName","Android"); desiredCapabilities.setCapability("appPackage","com.tencent.mm"); desiredCapabilities.setCapability("appActivity","com.tencent.mm.ui.LauncherUI"); //一定要注意!!!此配置一定要加 不清楚微信的数据 desiredCapabilities.setCapability("noReset",true); // 支持X5内核应用自动化配置 desiredCapabilities.setCapability("recreateChromeDriverSessions", true); // ChromeOptions使用来定制启动选项,因为在appium中切换context识别webview的时候, // 由于小程序是在一个单独的进程中,所以需要加上androidProcess: com.tencent.mm:appbrand0 ChromeOptions options = new ChromeOptions(); // 第二个参数要改成你查询到的小程序进程名 options.setExperimentalOption("androidProcess", "com.tencent.mm:appbrand0"); desiredCapabilities.setCapability(ChromeOptions.CAPABILITY, options); // 初始化会默认将chrome浏览器打开,需要将Browser置为空 desiredCapabilities.setBrowserName(""); URL url= new URL("http://127.0.0.1:4723/wd/hub");//http://127.0.0.1:4723/wd/hub androidDriver = new AndroidDriver(url,desiredCapabilities); //设置全局的隐式等待 androidDriver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); } @Test public void testLemon() throws InterruptedException { //在微信的首页向下滑动进入到小程序列表 Thread.sleep(8000); swipeDown(800); //选择对应的小程序-柠檬班软件测试 androidDriver.findElement(MobileBy.AndroidUIAutomator("new UiSelector().text(\"柠檬班软件…\")")).click(); Thread.sleep(10000); //进入到小程序页面之后-切换context Set<String> contexts = androidDriver.getContextHandles(); System.out.println(contexts); //切换context androidDriver.context("WEBVIEW_com.tencent.mm:appbrand0"); //坑·· 小程序打开后会有三个窗口,必须要切换窗口才可以在对应的页面找元素/操作元素 //获取打开的所有web窗口句柄 Set<String> handles = androidDriver.getWindowHandles(); //循环遍历集合切换正确的窗口 for(String handle : handles){ //条件判断:什么时候下才是正确的句柄--根据当前窗口的标题来判断 if(androidDriver.getTitle().equals("腾讯课堂柠檬班软件测试")){ //符合的,跳出循环 break; }else{ //切换句柄 androidDriver.switchTo().window(handle); } } //坑:微信使用的是77版本webview(X5内核),不能按照常规匹配模式去找对应的chromeDriver //微信小程序兼容的chromeDriver是V2.40 //定位元素 androidDriver.findElement(By.partialLinkText("课程")).click(); //web定位方式找元素/操作元素 } //向下滑动方法 public void swipeDown(int time){ //获取屏幕宽和高 int width = androidDriver.manage().window().getSize().getWidth(); int height = androidDriver.manage().window().getSize().getHeight(); //滑动的距离Or效果由什么决定?? //1、由起始点和终止点的距离 //2、滑动时间快慢 //起始点坐标:435 610 终止点坐标:435 1206 向下滑动 //(1)、创建touchAction对象 TouchAction touchAction = new TouchAction(androidDriver); //(2)把原始坐标转换成PointOption类型的对象 //起始点:屏幕1/2宽度 1/3高度位置 //终止点:屏幕1/2宽度 2/3高度位置 PointOption pointOption1 = PointOption.point(width/2,height/4); PointOption pointOption2 = PointOption.point(width/2,height*3/4); //(3)、通过touchAction完成一系列手势操作 //先去按压起始点->移动到终止点->手指释放-->让动作生效 //waitAction -->设置滑动的时间 WaitOptions Duration duration = Duration.ofMillis(time); WaitOptions waitOptions = WaitOptions.waitOptions(duration); touchAction.press(pointOption1).waitAction(waitOptions).moveTo(pointOption2).release().perform(); } }
3、失败用例截图
3.1 生成本地图片
public static void takeScreenshot(String filePath) { // 类型强制转换 TakesScreenshot takesScreenshot = (TakesScreenshot) driver; // OutputType.FILE -->返回类型为File(图片) File srcFile = takesScreenshot.getScreenshotAs(OutputType.FILE); // 把file(图片)保存到本地 // 给一个目标地址的File对象 File destFile = new File(filePath); // 把原始file对象拷贝到目标地址对应的file对象中 // 第三方依赖 commons-io try { FileUtils.copyFile(srcFile, destFile); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
3.2 Allure报表嵌入截图
1、创建AllureReportListener类实现IHookable接口
IHookable接口作用:动态替换每一个被@Test注解标注的方法
public class TestResultListener implements IHookable { //public int i = 1; @Override public void run(IHookCallBack iHookCallBack, ITestResult iTestResult) { //在执行的时候替换掉@Test注解标注的测试方法 //System.out.println("这里是监听器的run方法"); //我们的目的是为了监听测试结果的,不是为了禁止掉测试方法执行 //让测试方法可以正常的执行 iHookCallBack.runTestMethod(iTestResult); //监听测试的结果(失败的???) iTestResult--》保存测试的结果 if(iTestResult.getThrowable() != null){ //此时来截图 //OutputType.FILE --> 返回值类型File类型 File srcFile = BaseTest.androidDriver.getScreenshotAs(OutputType.FILE); //把File对象保存到本地,以图片格式 //每一次生成的截图名字都得要不一样 //System.out.println(iTestResult.get()); //如何获取到工程的根路径 String projectPath = System.getProperty("user.dir"); //时间戳 long time = System.currentTimeMillis(); //格式转换 File targetFile = new File(projectPath+"\\screenshot\\screenshot_"+ iTestResult.getName()+"_"+time+".png"); //i++; try { FileUtils.copyFile(srcFile,targetFile); } catch (IOException e) { e.printStackTrace(); } } } }
2、配置监听器-testng.xml,和test同级别-记得要运行这文件,不然不生效
<listeners>
<listener class-name="com.lemon.listener.AllureReportListener"/>
</listeners>