11.13 处理拖动
拖动就是将一个对象从一个位置拖到另外一个位置,可以简化桌面操作,如代码清单11-18所示。
代码清单11-18拖动
public void drag(By startElement_by, By endElement_by){ TouchAction act = new TouchAction(driver) ; //定位元素的原位置 MobileElement startElement = (MobileElement) driver.findElement(startElement_by); //定位元素要移动到的目标位置 MobileElement endElement = (MobileElement) driver.findElement(endElement_by) ; //执行元素的移动操作 act.press(startElement).perform(); act.moveTo(endElement).release().perform(); } |
11.14 处理截图
Appium可以通过使用getScreenshotAs截取整个页面作为图片,在测试过程中帮助我们直观地定位错误,如代码清单11-19所示。
代码清单11-19截图操作
WebElement RegisterPage=driver.findElement(By.name("startUserRegistration")); FilescreenShot=driver.getScreenshotAs(OutputType.FILE); Filelocation=newFile("screenshots"); String screenShotName=location.getAbsolutePath()+File.separator+"testCalculator.png"; try{ System.out.println("save screenshop"); FileUtils.copyFile(screenShot,new File(screenShotName));} catch(IOException e){ System.out.println("save screenshop fail"); e.printStackTrace(); } finally{ System.out.println("save screenshop finish"); } |
受到设备存储容量的限制,我们可以考虑扩展这个功能,使得它可以截取页面上某一个元素。要截取页面上的username编辑框,代码如代码清单11-20所示。
代码清单11-20截取指定元素
WebElement username = driver.findElementById("inputUsername"); username.sendKeys("bree"); getElementShotSaveAs(username); Assert.assertEquals("liming", username.getText()); public void getElementShotSaveAs(WebElement element) throws IOException{ File screenShot=driver.getScreenshotAs(OutputType.FILE); BufferedImage img = ImageIO.read(screenShot); int width = element.getSize().width; int height = element.getSize().height; Rectangle rect = new Rectangle(width,height); Point p = element.getLocation(); BufferedImage dest = img.getSubimage(p.x, p.y, rect.width, rect.height); ImageIO.write(dest, "png",screenShot); } |
由于自动化测试是无人值守的,因此可以利用TestNG监听器来实现监听功能。当测试处于某种状态的时候执行错误截图,如测试失败时的截图。这里采用testListenerAdapter方法,每次测试失败的时候,都会重写该方法。
新建两个类,一个用作监听器,另外一个用于写测试代码。
1.监听器
监听器是一些预定义的Java接口。用户创建这些接口的实现类,并把它们加入TestNG中,TestNG 便会在测试运行的不同时刻调用这些类中的接口方法。这里使用ITestListener监听器,实现其方法为onTestFailure在测试失败的时候,保存控件的截图,如代码清单11-21所示。
代码清单11-21监听器
packageappiumsample; importjava.io.File; importjava.io.IOException; importio.appium.java_client.AppiumDriver; importorg.apache.commons.io.FileUtils; importorg.openqa.selenium.OutputType; importorg.testng.ITestResult; importorg.testng.TestListenerAdapter; publicclassScreenshotListenerextendsTestListenerAdapter{ @Override publicvoidonTestFailure(ITestResulttr){ AppiumDriverdriver=Screenshot.getDriver(); Filelocation=newFile("screenshots"); StringscreenShotName=location.getAbsolutePath()+File.separator+tr.getMethod(). getMethodName()+".png"; //使用ItestResult获取失败方法名 FilescreenShot=driver.getScreenshotAs(OutputType.FILE); try{ FileUtils.copyFile(screenShot,newFile(screenShotName)); } catch(IOExceptione){ e.printStackTrace(); } } } |
2.测试代码
通过使用"@Listeners"注释,可以直接在 Java 源代码中添加 TestNG 监听器,如代码清单11-22所示。
代码清单11-22测试代码
package appiumsample; import io.appium.java_client.android.AndroidDriver; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import org.openqa.selenium.WebElement; import org.openqa.selenium.remote.DesiredCapabilities; import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Listeners; import org.testng.annotations.Test; @Listeners({ScreenshotListener.class}) public class Screenshot { private static AndroidDriver driver; @BeforeClass public void setup() throws MalformedURLException { //App地址 String apppath = "F:\\selendroid-test-app-0.15.0_debug.apk"; //配置AndroidDriver DesiredCapabilities capabilities = new DesiredCapabilities(); capabilities.setCapability("deviceName", "Lenovo A788t");//真机的名称为Lenovo A788t capabilities.setCapability("platformVersion", "4.3");//操作系统版本为4.3 capabilities.setCapability("platformName", "Android");//操作系统名称为Android capabilities.setCapability("udid", "00a10399");//使用的真机为Android平台 capabilities.setCapability("app", apppath);//确定待测App capabilities.setCapability("appPackage", "io.selendroid.testapp");//待测App包 capabilities.setCapability("appActivity", ".HomeScreenActivity");//待测App主Activity名 capabilities.setCapability("automationName", "selendroid"); driver = new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"),capabilities); // WebDriverWait wait = new WebDriverWait(driver,10); } @SuppressWarnings("deprecation") @Test public void testExample() throws IOException { WebElement username = driver.findElementById("inputUsername"); username.sendKeys("bree"); Assert.assertEquals("liming", username.getText()); } public static AndroidDriver getDriver(){ return driver; } @AfterClass public void tearDown(){ } } |
11.15 隐式等待
在运行测试时,测试可能并不总是以相同的速度响应,例如,可能在几秒后进度条到100%时,按钮才会变成可单击的状态。这里介绍不同的方法进行同步测试。
隐式等待有两种方法,即implicitlyWait和sleep。需要注意的是,一旦设置了隐式等待,则它存在整个driver对象实例的生命周期中。在下例中,设置全局等待时间是30s,这是最长的等待时间。
最直接的方式是设置固定的等待时间。
Thread.sleep(30000)
对于固定等待时间的元素,可以用sleep进行简单的封装来实现等待指定的时间,如代码清单11-23所示。
代码清单11-23用sleep实现等待
@Test(description = "sleep简单封装") private boolean testisElementPresent(By by) throws InterruptedException { try { //设置等待时间 Thread.sleep(1000); //查找元素 driver.findElement(by); //若找到元素,返回true return true; } catch (NoSuchElementException e) { //若找不到元素,返回false return false; } } |
也可以利用sleep封装一个计时器,完成等待操作,如代码清单11-24所示。
代码清单11-24通过sleep计时器实现隐式等待
@Test(description = "sleep封装") public static void testwaitTimer( int units, int mills ) { DecimalFormat df = new DecimalFormat("###.##"); double totalSeconds = ((double)units*mills)/1000; System.out.print("Explicit pause for " + df.format(totalSeconds) + " seconds divided by " + units + " units of time: "); try { Thread.currentThread(); int x = 0; while( x < units ) { Thread.sleep( mills ); System.out.print("." ); x = x + 1; } System.out.print('\n'); } catch ( InterruptedException ex ) { ex.printStackTrace(); } } |
隐式等待方式(implicitlyWait)是指在尝试发现某个元素的时候,如果没能立刻发现,就等待固定长度的时间。默认设置是0s,如代码清单11-25所示。
代码清单11-25implicitlyWait实现隐式等待
@Test(description = "测试显示等待") public void testImplicitlyWait(){ //识别"美食"图标 MobileElement meishiElement = (MobileElement) driver.findElement(By. xpath("//android. support.v7.widget.RecyclerView/android.widget.RelativeLayout[1]/android.widget. ImageView")); //单击"美食"图标,跳转到"美食"界面 meishiElement.click(); //设置全局等待时间最大为30s driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS); //查找"深***" try{ //查找"深***" driver.findElement(By.xpath("//android.widget.TextView[@text='深***']")); }catch(NoSuchElementException e){ //如果控件没有找到,则测试用例执行失败 Assert.fail("没有找到控件"); } } |
11.16 显示等待方法
在自动化测试的过程中,很多窗体内的数据,需要等待一会儿,才能加载完数据,才能出现一些元素,Driver才能操作这些元素。另外,做一些操作,本身可能也需要等待一会儿才有数据显示。
不管是否加载完成,隐式等待都会等待特定的时间,它会让一个正常响应的应用的测试变慢,增加了整个测试执行的时间。比如有些控件可能数据较多,需要较长时间才可以加载完成,但是其他控件加载很快,把它们都设置成固定等待时间,将会造成大量时间的浪费。因此,合理地设置时间等待是非常必要的。
Appium中提供了AppiumFluentWait来实现显示等待。AppiumFluentWait继承自FluentWait。这个类能支持一直等待知道特定的条件出现,使用AppiumFluentWait可以设置最大等待时间、等待的频率等,如代码清单11-26所示。
代码清单11-26显示等待
@Test(description = "测试FluentWait") public void testFluent(){ //识别美团图标 MobileElement meituan = (MobileElement) driver.findElement(By.xpath("// android.support. v7.widget.RecyclerView/android.widget.RelativeLayout[1]/android.widget.ImageView")); //创建AppiumFluentWait对象 new AppiumFluentWait<MobileElement>(meituan) //最长等待时间为10s .withTimeout(10, TimeUnit.SECONDS) //每隔100ms判断一次元素的文本值是否为"深***" .pollingEvery(100,TimeUnit.MILLISECONDS) .until(new Function<MobileElement,Boolean>(){ @Override public Boolean apply(MobileElement element) { return element.getText().endsWith("深***"); } }); } |
AppiumFluentWait的until的参数可以是Predicate,也可以是Function。这里使用的是Function。因为Function的返回值种类较多,可以为Object或者Boolean类型,而Predicate只能返回Boolean类型。
11.17 在编程中处理adb命令
在对App进行性能测试时,如获取CPU信息的命令为adb shell dumpsys cpuinfo packagename。在selendroid-test-app-0.15.0.apk实例中,要获取CPU的性能指标,编写的代码如代码清单11-27所示。
代码清单11-27获取CPU的性能指标
public static void GetCpu(String packageName) throws IOException { Runtime runtime = Runtime.getRuntime(); Process proc = runtime.exec("adb shell dumpsys cpuinfo $"+packageName); try { if (proc.waitFor() != 0) { System.err.println("exit value = " + proc.exitValue()); } BufferedReader in = new BufferedReader(new InputStreamReader( proc.getInputStream())); String line = null; String totalCpu = null; String userCpu = null; String kernalCpu = null; while ((line = in.readLine()) != null) { if(line.contains(packageName)){ System.out.println(line); totalCpu = line.split("%")[0].trim(); userCpu = line.substring(line.indexOf(":")+1,line.indexOf("% user")).trim(); kernalCpu = line.substring(line.indexOf("+")+1, line.indexOf("% kernel")).trim(); System.out.printf("totalCpu的值为:%s%n", totalCpu); System.out.printf("userCpu的值为:%s%n", userCpu); System.out.printf("kernalCpu的值为:%s%n", kernalCpu); } } } catch (InterruptedException e) { System.err.println(e); }finally{ try { proc.destroy(); } catch (Exception e2) { } } } |
输出结果如图11-7所示。
图11-7 CPU性能指标
在实际的测试过程中可以多次调用上述代码,以获取不同阶段的CPU值。其他性能指标的获取方法类似。
版权声明:51Testing软件测试网获人民邮电出版社和作者授权连载本书部分章节。
任何个人或单位未获得明确的书面许可,不得对本文内容复制、转载或进行镜像,否则将追究法律责任。