在编程中处理adb命令—App自动化测试与框架实战(10)

发表于:2019-4-01 13:15

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:刘金起,李明黎    来源:51Testing软件测试网原创

  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软件测试网获人民邮电出版社和作者授权连载本书部分章节。
任何个人或单位未获得明确的书面许可,不得对本文内容复制、转载或进行镜像,否则将追究法律责任。
21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号