第11章 Appium常见问题处理方式
本章对自动化测试中的常见问题进行总结,供平时遇到问题时进行参考。
11.1 输入中文
在使用Appium做手机端的自动化测试时,会遇到输入中文的问题。常见的解决方式如下。
(1)把测试类另存为UTF-8格式。
(2)在Desired Capabilities中增加两个属性:unicodeKeyboard和resetKeyboard。
capabilities.setCapa bility("unicodeKeyboard", true); capabilities.setCapability("resetKeyboard", true); |
在脚本的执行过程中可以直接输入中文,程序执行成功。
username.sendKeys("视界");
11.2 滑动操作
Appium通过swipe函数处理滑动问题,如代码清单11-1所示。从代码可以看出,此方法共有5个参数,都是整型,依次是起始位置的x、y坐标和终点位置的x、y坐标,以及滑动间隔时间,单位为ms。
代码清单11-1swipe函数
public void swipe(int startx, int starty, int endx, int endy, int duration) { TouchAction touchAction = new TouchAction(this); //Appium把press-wait-moveto-release转换为滑动操作 touchAction.press(startx, starty).waitAction(duration) .moveTo(endx, endy).release(); touchAction.perform(); } |
为了让Appium更好地兼容不同分辨率的设备,需要在滑动前先获取屏幕的分辨率。
int width = driver.manage().window().getSize().width;
int height = driver.manage().window().getSize().height;
然后根据屏幕的分辨率执行各种滑动操作。
//向上滑动 driver.swipe(width / 2, height * 3 / 4, width / 2, height / 4, during); //向下滑动 driver.swipe(width / 2, height / 4, width / 2, height * 3 / 4, during); //向左滑动 driver.swipe(width / 4, height / 2, width * 3 / 4, height / 2, during); //向右滑动 driver.swipe(width * 3 / 4, height / 2, width / 4, height / 2, during); |
以大众点评网的App为例,在大众点评网首页中,单击"美食"图标(如图11-1所示),弹出图11-2所示界面。
"美食"界面罗列了很多美食的信息。"深×××"不在"美食"界面中,需要向上滑动页面,让"深×××"显示出来并单击进入"深×××"对应的列表项。
实现的代码如代码清单11-2所示。
代码清单11-2大众点评网示例
public void testSwip() throws InterruptedException{ boolean up; //美食图标 MobileElement meishiElement = (MobileElement) driver.findElement(By.xpath ("// android.support.v7.widget.RecyclerView/android.widget.RelativeLayout[1] /android.widget.ImageView")); //单击美食图标,跳转到美食界面 meishiElement.click(); //获取当前页面的屏幕尺寸 int width = driver.manage().window().getSize().width; int height = driver.manage().window().getSize().height; while(true){ try{ MobileElement endElement = (MobileElement) driver.findElement(By.xpath ("//android.widget.TextView[@text='深×××']")); endElement.click(); break; }catch(Exception e){ driver.swipe(width / 2, height * 3 / 4, width / 2, height / 4, 1000); Thread.sleep(2000); } } } |
11.3 滚动操作
Appium早期版本提供了Scroll方法来实现滚动,最新的版本已经取消了这个方法。笔者使用findElementByAndroidUIAutomator()方法来实现同样的功能,如代码清单11-3所示。
代码清单11-3滚动操作的实现
public void scrollToElement(String str) { ((AndroidDriver<MobileElement>) driver).findElementByAndroidUIAutomator( "new UiScrollable(new UiSelector().scrollable(true).instance(0)).ScrollIntoView (new UiSelector().textContains(\""+ str + "\").instance(0))"); } public void scrollToExactElement(String str) { ((AndroidDriver<MobileElement>) driver).findElementByAndroidUIAutomator( "new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView (new UiSelector().textContains(\""+ str + "\").instance(0))"); } |
findElementByAndroidUIAutomator()要求传入的参数为字符串。
UiSelector().scrollable(true) .instance(0)表示找到一个可以滑动的对象,通过判断scrollable属性是否为true进行查找。
scrollIntoView滑动到匹配的selector控件,如果没有匹配到,则停留在滑动列表的最下方。
在new UiSelector().text(string).instance(0)待匹配的selector控件中,设置text属性值为指定的string。
UiSelector().textContains(string).instance(0))待匹配的selector控件,指定text属性值包含指定的string。
11.4 输入Android按键
在清除编辑框的内容时,采用以下步骤。
(1)获取编辑框中文本的长度。
(2)将光标移动到文本的尾部。
(3)按退格直到所有文本被删除。
如代码清单11-4所示,参考6.2.2节,按键123表示光标移动到末尾,按键67表示退格键。
代码清单11-4输入Android按键
/** * 描述:清理文本框 * 参数:文本框的内容 */ public void clearText(String text) { for (int i = 0; i < text.length(); i++) { //123:KEYCODE_MOVE_END 光标移动到末尾 driver.pressKeyCode(123); //KEYCODE_DEL 退格键 67 driver.pressKeyCode(67); } } } |
11.5 处理Popup Window
Popup Window是一个弹出窗口控件,可以用来显示任意视图(View),而且会浮动在当前活动(Activity)的顶部。通过UI Automator Viewer无法识别,通过Hierarchy Viewer才可以识别到Popup Window。这属于系统逻辑,Appium暂时没有处理机制,可以采用下面的方法进行解决。
" 通过Tap方法,代码如下。
WebElement imagebtn = driver.findElementById("showPopupWindowButton"); magebtn.click(); driver.tap(1, 214, 475, 10); |
" 通过TouchAction,代码如下。
WebElement imagebtn2 = driver.findElementById("showPopupWindowButton"); imagebtn2.click(); TouchAction action = new TouchAction(driver); action.press(214, 475).release().perform(); |
对于需要操作的Popup Window,用户可以首先获得坐标点,然后以封装为Hash函数的方式进行操作。例如弹出框上有4个蓝色的球,要获取每个球的坐标点,可以封装这4个坐标点到一个Hash函数中,如代码清单11-5所示,在其他方法中通过调用Hash函数实现对任意蓝色的球进行操作,如代码清单11-6所示。这里采用的是绝对坐标,考虑到分辨率的问题,建议对相对坐标进行封装。
代码清单11-5获取坐标点并封装
package appiumsample; import java.util.*; import org.openqa.selenium.Point; public class Popuppointer { //获得蓝球的坐标 public void blueball(){ Point x1 = new Point(1013, 534); Point x2 = new Point(1114,534); Point x3 = new Point(1210,534); Point x4 = new Point(1294,534); HashMap<Integer, Point> blueball= new HashMap<Integer, Point>(); blueball.put(1,x1); blueball.put(2,x2); blueball.put(3,x3); blueball.put(4,x4); } } |
调用方法如代码清单11-6所示。
代码清单11-6调用Hash函数
driver.tap(1,Popuppointer.blueball().get(1).x,Popuppointer.blueball().get(1).y,30); driver.tap(1,Popuppointer.blueball().get(2).x,Popuppointer.blueball().get(2).y,30); driver.tap(1,Popuppointer.blueball().get(3).x,Popuppointer.blueball().get(3).y,30); driver.tap(1,Popuppointer.blueball().get(4).x,Popuppointer.blueball().get(4).y,30); |
Popup Window无法识别,很大程度上是因为焦点无法找到,以上方法仅适用于无源码的情况。
11.6 处理Toast
Toast是Android中用来显示信息的一种机制,和Dialog不一样的是,Toast是没有焦点的,而且Toast显示的时间有限,过一定的时间就会自动消失。此外,它主要用于向用户显示提示消息。Toast属于Android系统逻辑,不属于应用逻辑,所以通过UI Automator Viewer无法获取控件。可以采用下面的方法进行解决。
1.图片比对方法
在Toast被触发后,截取界面的图片,然后进行比对,如代码清单11-7所示。
代码清单11-7对比图片
WebElement imagebtn = driver.findElementById("showToastButton"); imagebtn.click(); try{ File scrFile =driver.getScreenshotAs(OutputType.FILE); FileUtils.copyFile(scrFile, new File("D:\\scrshot.png")); } catch(Exception e){ e.printStackTrace(); } |
通过getScreenshotAs方法来捕捉屏幕,如代码清单11-7所示,使用OutputType.FILE作为参数传递给getScreenshotAs,告诉它将截取的屏幕以文件的形式返回。使用copyFile保存getScreenshotAs截取的屏幕文件到D盘中并命名为scrshot.png。
因为Toast一般显示有限的时间就会自动消失,所以在截取图片的时候,建议多截取几张,以保证截取成功。
2.Seledroid方法
Seledroid方法(自动化测试引擎)可以识别Toast控件,采用WaitForElement方法获得Toast上的文本。
waitForElement(By.partialLinkText("Hello seledroid toast"), 4, driver);
3.使用Automator2
在最新版本的Appium中,使用Automator2自动化测试引擎,可以获取Toast。升级Appium为GUI 1.2.3版本,如代码清单11-8所示。
代码清单11-8在Appium中处理Toast
package com.shijie.testScripts; import static org.testng.Assert.assertNotNull; import io.appium.java_client.android.Activity; import io.appium.java_client.android.AndroidDriver; import java.io.File; import java.net.URL; import java.util.concurrent.TimeUnit; import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import io.appium.java_client.remote.AutomationName; import io.appium.java_client.remote.MobileCapabilityType; public class testkongjian { AndroidDriver<WebElement> driver; @BeforeMethod public void SetUp() throws Exception { File appDir = new File("F:\\"); File app = new File(appDir, "selendroid-test-app-0.17.0.apk"); DesiredCapabilities capabilities = new DesiredCapabilities(); capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator"); capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.ANDROID_UIAUTOMATOR2); driver = new AndroidDriver<>(new URL("http://127.0.0.1:4723/wd/hub"), capabilities); driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS); } @Test public void test_toast(){ Activity activity = new Activity("io.selendroid.testapp", ".HomeScreenActivity"); driver.startActivity(activity); WebElement toastButton = null; toastButton = driver.findElement(By.id("io.selendroid.testapp:id/showToastButton")); toastButton.click(); final WebDriverWait wait = new WebDriverWait(driver, 10); assertNotNull(wait.until(ExpectedConditions.presenceOfElementLocated( By.xpath("//*[@text='Hello selendroid toast!']")))); } @AfterMethod public void TearDown() { driver.quit(); } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub } } |
版权声明:51Testing软件测试网获人民邮电出版社和作者授权连载本书部分章节。
任何个人或单位未获得明确的书面许可,不得对本文内容复制、转载或进行镜像,否则将追究法律责任。