处理截图
通过监听器
java
package org.davieyang.testscripts;
import java.io.File;
import java.io.IOException;
import io.appium.java_client.AppiumDriver;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import org.testng.ITestResult;
import org.testng.TestListenerAdapter;
public class ScreenshotListener extends TestListenerAdapter{
/**
* 监听器是一些预定义的java接口,创建这些接口的实现类
* 然后将其加入TestNG中,TestNG会在测试运行的不同时刻调用这些类中的接口方法
* 实现ITestListener监听器的onTestFailure在测试失败的时候,保存控件截图
*/
@Override
public void onTestFailure(ITestResult iTestResult) {
super.onTestFailure(iTestResult);
AppiumDriver driver = Screenshot.getDriver();
File file = new File("screenshots");
String screenShotName = file.getAbsolutePath() + File.separator + iTestResult.getMethod().getMethodName()+".png";
File screenShot = driver.getScreenshotAs(OutputType.FILE);
try{
FileUtils.copyFile(screenShot, new File(screenShotName));
}catch (IOException e){
e.printStackTrace();
}
}
}
截图的几种尝试
java
package org.davieyang.testscripts;
import java.io.File;
import java.io.IOException;
import io.appium.java_client.AppiumDriver;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import org.testng.ITestResult;
import org.testng.TestListenerAdapter;
public class ScreenshotListener extends TestListenerAdapter{
/**
* 监听器是一些预定义的java接口,创建这些接口的实现类
* 然后将其加入TestNG中,TestNG会在测试运行的不同时刻调用这些类中的接口方法
* 实现ITestListener监听器的onTestFailure在测试失败的时候,保存控件截图
*/
@Override
public void onTestFailure(ITestResult iTestResult) {
super.onTestFailure(iTestResult);
AppiumDriver driver = Screenshot.getDriver();
File file = new File("screenshots");
String screenShotName = file.getAbsolutePath() + File.separator + iTestResult.getMethod().getMethodName()+".png";
File screenShot = driver.getScreenshotAs(OutputType.FILE);
try{
FileUtils.copyFile(screenShot, new File(screenShotName));
}catch (IOException e){
e.printStackTrace();
}
}
}
处理Toast
Toast是Android中用来显示信息的一种机制,和Dialog不一样,它没有焦点,而且显示的时间有限,很快就消失,Toast是Android的系统特性,不是应用特性,因此通过UI Automator Viewer无法获取控件
图片对比
Toast被触发后,截图对比
java
WebElement imagebtn = driver.findElementById("ShowToastButton");
imagebtn.click();
try{
// 通过getScreenshotAs方法来捕捉屏幕,使用OutputType.File作为参数传递给getScreenshotAs方法
// 告诉它将截取的屏幕以文件的形式返回
File scrFile = driver.getScreenshotAs(OutpuType.File);
// 使用copyFile保存getScreenshotAs截取的屏幕文件到D盘中并命名为scrshot.png
FileUtils.copyFile(scrFile, new File("D:\\scrshot.png"));
}catch(Exception e){
e.printStackTrace();
}
Selendroid方法
Selendroid方法(自动化测试引擎)可以识别Toast控件,采用WaitForElement方法获得Toast文本
java
waitForElement(By.partialLinkText("Hello seledroid toast"), 4, driver);
Automator2
在新版本的Appium中,使用Automator2自动化测试引擎,可以获取Toast
java
package org.davieyang.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.ExpectedCondition;
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 ToastDemo {
AndroidDriver<WebElement> driver;
@BeforeMethod
public void setUp()throws Exception{
File appDir = new File("D:\\");
File app = new File(appDir, "selendroid-test-app-0.17.0.apk");
DesiredCapabilities cap = new DesiredCapabilities();
cap.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator");
cap.setCapability(MobileCapabilityType.APP, app.getAbsolutePath());
cap.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.ANDROID_UIAUTOMATOR2);
driver = new AndroidDriver<>(new URL("http://127.0.0.1:4723/wd/hub"), cap);
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
}
@Test
public void testToast(){
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();
}
}
并行测试
Appium提供了一种方式以在一台设备上自动操作多个会话,也就是采用多个标识符的方式启动多个Appium服务器端从而实现测试并行执行,例如启动两个Appium服务器,第一个服务器Server Port设置为4723
,Bootstrap Port设置为4724
;第二个服务器Server Port设置为4725
,Bootstrap Port设置为4726
提取Desired Capabilities
java
package org.davieyang;
public class Constants {
public class RedMi3{
public static final String deviceName = "Redmi3";
public static final String udid = "claeae297d72";
public static final String platformVersion = "5.1.1";
public static final String platformName = "Android";
public static final String appPackage = "io.selendroid.testapp";
public static final String appActivity = ".HomeScreenActivity";
public static final String unicodeKeyboard = "True";
public static final String noSign = "True";
}
public class RedMi4{
public static final String deviceName = "Redmi4";
public static final String udid = "claeae297d73";
public static final String platformVersion = "6.1.1";
public static final String platformName = "Android";
public static final String appPackage = "io.selendroid.testapp";
public static final String appActivity = ".HomeScreenActivity";
public static final String unicodeKeyboard = "True";
public static final String noSign = "True";
}
}
测试代码
java
package org.davieyang.testscripts;
import java.net.URL;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.AndroidElement;
import org.testng.Assert;
import org.davieyang.Constants;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
public class TestParallelRunDemo1 {
AndroidDriver<AndroidElement> driver;
@BeforeMethod
@Parameters({"device_ID", "port"})
public void setUp() throws Exception{
// Desired Capabilities;
// Define driver
}
@Test
public void testWebApp(){
System.out.println("TestScripts");
}
@AfterMethod
public void TearDown(){
driver.quit();
}
}
java
package org.davieyang.testscripts;
import java.net.URL;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.AndroidElement;
import org.testng.Assert;
import org.davieyang.Constants;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.NoSuchElementException;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
import org.openqa.selenium.By;
public class TestParallelrundemo2 {
AndroidDriver<AndroidElement> driver;
@BeforeMethod
@Parameters({"device_ID", "port"})
public void setUp() throws Exception{
// Desired Capabilities;
// Define driver
}
@Test
public void testWebApp(){
System.out.println("TestScripts");
}
@AfterMethod
public void TearDown(){
driver.quit();
}
}
配置testng.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="All Test Suite" parallel="tests" thread-count="2">
<test verbose="2" preserve-order="true" name="parallelRunDemo1">
<parameter name="device_ID" value="127.0.0.1"/>
<parameter name="port" value="4723"/>
<classes>
<class name="org.davieyang.testscripts.TestParallelRunDemo1">
<methods>
<include name="testWebApp"/>
</methods>
</class>
</classes>
</test>
<test verbose="2" preserve-order="true" name="parallelRunDemo2">
<parameter name="device_ID" value="127.0.0.1"/>
<parameter name="port" value="4725"/>
<classes>
<class name="org.davieyang.testscripts.TestParallelrundemo2">
<methods>
<include name="testWebApp"/>
</methods>
</class>
</classes>
</test>
</suite>
日志系统
在开始之前请确保已经掌握
JavaApp自动化测试系列[v1.0.0][Appium数据驱动测试框架]
JavaApp自动化测试系列[v1.0.0][Appium数据驱动测试框架之公共类库]
单元测试系列[v1.0.0][Log4j]
配置POM
xml
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
配置log4j.properties
xml
log4j.rootLogger=info, toConsole, toFile
log4j.appender.file.encoding=UTF-8
log4j.appender.toConsole=org.apache.log4j.ConsoleAppender
log4j.appender.toConsole.Target=System.out
log4j.appender.toConsole.layout=org.apache.log4j.PatternLayout
log4j.appender.toConsole.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss}] [%p] %m%n
log4j.appender.toFile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.toFile.file=result/log/testlog.log
log4j.appender.toFile.append=false
log4j.appender.toFile.Threshold=info
log4j.appender.toFile.layout=org.apache.log4j.PatternLayout
log4j.appender.toFile.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss}] [%p] %m%n
测试代码引入日志
java
package com.davieyang.testscripts;
import com.davieyang.util.DataProviderFromCsv;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.AndroidElement;
import com.davieyang.pages.HomePage_PF;
import com.davieyang.pages.RegisterPage_PF;
import com.davieyang.pages.RegisterVerifyPage_PF;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.apache.log4j.Logger;
import com.davieyang.base.BaseActivity;
import com.davieyang.util.Constants;
public class TestRegisterIII extends BaseActivity{
Logger log = Logger.getLogger(TestRegisterIII.class.getName());
AndroidDriver<AndroidElement> driver;
@DataProvider(name="RegisterData", parallel = true)
public static Object[][] getRegisterData() throws Exception{
return DataProviderFromCsv.getTestData(Constants.注册页面I.filepath+ "/" +Constants.注册页面I.filename);
}
@Test(dataProvider = "RegisterData")
public void test_Register_success(String no, String testName, String username, String eMail, String password, String name,
String prolanguage, String accept, String verifyusername, String verifyeMail, String verifypassword,
String verifyname, String verifyprolanguage, String verifyaccept) throws IllegalArgumentException, Exception{
HomePage_PF homePage_pf = null;
RegisterPage_PF registerPage_pf = null;
RegisterVerifyPage_PF registerVerifyPage_pf = null;
try{
log.info("初始化home页面");
homePage_pf = new HomePage_PF(getDriver());
log.info("点击注册按钮,跳转到注册页面");
registerPage_pf = homePage_pf.navigate_register_page();
log.info("调用注册成功方法,传入参数");
registerVerifyPage_pf = registerPage_pf.register_sucess(username, eMail, password, name, prolanguage, accept);
}catch (AssertionError error){
log.info("调用注册成功方法失败");
Assert.fail("调用注册成功方法失败");
}
try{
log.info("断言注册验证页面是否包含输入的name值");
Assert.assertEquals(registerVerifyPage_pf.get_name_value(), name);
}catch (AssertionError nameerror){
log.info("断言失败");
Assert.fail("name断言失败,查看name值是否正确");
}
try{
log.info("断言注册验证页面是否包含输入的username值");
Assert.assertEquals(registerVerifyPage_pf.get_username_value(), username);
}catch (AssertionError usernameerror){
log.info("断言失败");
Assert.fail("username断言失败,查看username值是否正确");
}
try{
log.info("断言注册验证页面是否包含输入的password值");
Assert.assertEquals(registerVerifyPage_pf.get_password_value(), password);
}catch (AssertionError passworderror){
log.info("断言失败");
Assert.fail("password断言失败,查看password值是否正确");
}
try{
log.info("断言注册验证页面是否包含输入的preferedProgrammingLanguage值");
Assert.assertEquals(registerVerifyPage_pf.get_preferedProgrammingLanguage_value(), prolanguage);
}catch (AssertionError prolanguageerror){
log.info("断言失败");
Assert.fail("prolanguage断言失败,查看prolanguage值是否正确");
}
try{
log.info("断言注册验证页面是否包含输入的email值");
Assert.assertEquals(registerVerifyPage_pf.get_email_value(), eMail);
}catch (AssertionError eMailerror){
log.info("断言失败");
Assert.fail("eMail断言失败,查看eMail值是否正确");
}
try{
log.info("断言注册验证页面是否包含输入的accept值");
Assert.assertEquals(registerVerifyPage_pf.get_acceptAdds_value(), accept);
}catch (AssertionError accepterror){
log.info("断言失败");
Assert.fail("accept断言失败,查看accept值是否正确");
}
}
}
页面对象引入日志
HomePage
java
package com.davieyang.pages;
import io.appium.java_client.android.AndroidDriver;
// PageFactory
import org.openqa.selenium.support.PageFactory;
import io.appium.java_client.pagefactory.AppiumFieldDecorator;
import io.appium.java_client.pagefactory.AndroidFindBy;
import io.appium.java_client.pagefactory.iOSXCUITFindBy;
import io.appium.java_client.MobileElement;
import java.util.logging.Logger;
public class HomePage_PF {
AndroidDriver<?> driver;
Logger log = Logger.getLogger(HomePage_PF.class.getName());
public HomePage_PF(AndroidDriver<?> driver){
this.driver = driver;
PageFactory.initElements(new AppiumFieldDecorator(driver), this);
}
// 页面对象时别,进入注册页面
@AndroidFindBy(id="startUserRegistration")
@iOSXCUITFindBy(id="startUserRegistration")
public MobileElement startRegister_btn;
// 单击注册页面按钮,进入注册页面,返回注册页面对象
public RegisterPage_PF navigate_register_page(){
log.info("在HomePage里点击按钮,进入register页面");
this.startRegister_btn.click();
return new RegisterPage_PF(driver);
}
}
RegisterPage
java
package com.davieyang.pages;
import java.util.List;
import java.util.logging.Logger;
import io.appium.java_client.android.AndroidDriver;
import org.openqa.selenium.support.PageFactory;
import io.appium.java_client.pagefactory.AppiumFieldDecorator;
import io.appium.java_client.pagefactory.AndroidFindBy;
import io.appium.java_client.pagefactory.iOSXCUITFindBy;
import io.appium.java_client.MobileElement;
public class RegisterPage_PF {
Logger log = Logger.getLogger(RegisterPage_PF.class.getName());
AndroidDriver<?> driver;
public RegisterPage_PF(AndroidDriver<?> driver){
this.driver = driver;
PageFactory.initElements(new AppiumFieldDecorator(driver), this);
}
// 页面对象时别
// 用户名
@AndroidFindBy(id="inputUsername")
@iOSXCUITFindBy(id="inputUsername")
public MobileElement username_txt;
// 邮箱
@AndroidFindBy(id="inputEmail")
@iOSXCUITFindBy(id="inputEmail")
public MobileElement email_txt;
// 密码
@AndroidFindBy(id="inputPassword")
@iOSXCUITFindBy(id="inputPassword")
public MobileElement password_txt;
// 姓名
@AndroidFindBy(id="inputName")
@iOSXCUITFindBy(id="inputName")
public MobileElement name_txt;
// 编程语言
@AndroidFindBy(id="input_preferedProgrammingLanguage")
@iOSXCUITFindBy(id="input_preferedProgrammingLanguage")
public MobileElement language_sel;
public List<MobileElement> prgLanguage;
// 是否确认注册
@AndroidFindBy(id="input_adds")
@iOSXCUITFindBy(id="input_adds")
public MobileElement accept_check;
// 注册按钮
@AndroidFindBy(id="btnRegisterUser")
@iOSXCUITFindBy(id="btnRegisterUser")
public MobileElement register_btn;
public RegisterVerifyPage_PF register_sucess(String username, String email, String password, String name,
String language, String check){
log.info("在Register页面输入username");
this.username_txt.sendKeys(username);
log.info("在Register页面输入email");
this.email_txt.sendKeys(email);
log.info("在Register页面输入password");
this.password_txt.sendKeys(password);
log.info("在Register页面清空name编辑框");
this.name_txt.clear();
log.info("在Register页面输入name");
this.name_txt.sendKeys(name);
log.info("在Register页面单击选择语言,弹出语言选择框");
this.language_sel.click();
log.info("在Register页面,弹出的语言选择框,选择语言");
checkedTextView(language);
log.info("在Register页面,选择accept_check");
if(check.equals("Yes")){
if(!this.accept_check.isSelected())
this.accept_check.click();
}
log.info("在Register页面,单击注册");
this.register_btn.click();
return new RegisterVerifyPage_PF(driver);
}
public void checkedTextView(String language){
// 使用class属性选择所有的单选按钮,并存放在一个list中
@SuppressWarnings("unchecked")
List<MobileElement> checkTextViews = (List<MobileElement>) driver
.findElementsByClassName("android.widget.CheckedTextVeiw");
// 使用for循环将List中的每个单选按钮进行遍历,查找name值为Ruby的单选按钮
// 如果该按钮未处于选中状态则调用click方法进行选择
for (MobileElement checkedTextView:checkTextViews){
if(checkedTextView.getAttribute("name").equals(language)){
if(!checkedTextView.isSelected()){
checkedTextView.click();
}
}
}
}
}
RegisterVerifyPage
java
package com.davieyang.pages;
import org.openqa.selenium.By;
import io.appium.java_client.android.AndroidDriver;
import org.openqa.selenium.support.PageFactory;
import io.appium.java_client.pagefactory.iOSXCUITFindBy;
import io.appium.java_client.pagefactory.AndroidFindBy;
import io.appium.java_client.pagefactory.AppiumFieldDecorator;
import io.appium.java_client.MobileElement;
import java.util.logging.Logger;
public class RegisterVerifyPage_PF {
Logger log = Logger.getLogger(RegisterVerifyPage_PF.class.getName());
AndroidDriver<?> driver;
public RegisterVerifyPage_PF(AndroidDriver<?> driver){
this.driver = driver;
PageFactory.initElements(new AppiumFieldDecorator(driver), this);
}
// 页面对象识别
@AndroidFindBy(id="label_name_data")
@iOSXCUITFindBy(id="label_name_data")
public MobileElement label_name_data;
// 页面对象识别
@AndroidFindBy(id="label_username_data")
@iOSXCUITFindBy(id="label_username_data")
public MobileElement label_username_data;
// 页面对象识别
@AndroidFindBy(id="label_password_data")
@iOSXCUITFindBy(id="label_password_data")
public MobileElement label_password_data;
// 页面对象识别
@AndroidFindBy(id="label_email_data")
@iOSXCUITFindBy(id="label_email_data")
public MobileElement label_email_data;
// 页面对象识别
@AndroidFindBy(id="label_preferedProgrammingLanguage_data")
@iOSXCUITFindBy(id="label_preferedProgrammingLanguage_data")
public MobileElement label_preferedProgrammingLanguage_data;
// 页面对象识别
@AndroidFindBy(id="label_acceptAdds_data")
@iOSXCUITFindBy(id="label_acceptAdds_data")
public MobileElement label_acceptAdds_data;
// 增加验证项,返回验证值
public String get_name_value(){
log.info("在VerifyRegisterPage页面通过输入获取name值");
return this.label_name_data.getTagName().toString();
}
// 增加验证项,返回验证值
public String get_username_value(){
log.info("在VerifyRegisterPage页面通过输入获取username值");
return this.label_username_data.getTagName().toString();
}
// 增加验证项,返回验证值
public String get_password_value(){
log.info("在VerifyRegisterPage页面通过输入获取password值");
return this.label_password_data.getTagName().toString();
}
// 增加验证项,返回验证值
public String get_email_value(){
log.info("在VerifyRegisterPage页面通过输入获取email值");
return this.label_email_data.getTagName().toString();
}
// 增加验证项,返回验证值
public String get_preferedProgrammingLanguage_value(){
log.info("在VerifyRegisterPage页面通过输入获取preferedProgrammingLanguage值");
return this.label_preferedProgrammingLanguage_data.getTagName().toString();
}
// 增加验证项,返回验证值
public String get_acceptAdds_value(){
log.info("在VerifyRegisterPage页面通过输入获取acceptAdds值");
return this.label_acceptAdds_data.getTagName().toString();
}
}
公共类库引入日志
java
package com.davieyang.base;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.AndroidElement;
import java.net.URL;
import java.util.logging.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.testng.ITestContext;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
public class BaseActivity {
public static Logger log = Logger.getLogger(BaseActivity.class.getName());
AndroidDriver<AndroidElement> driver;
@BeforeClass
public void startTest(ITestContext context) throws Exception{
PropertyConfigurator.configure("config/log4j.properties");
log.info("--------测试开始----------");
DesiredCapabilities cap = new DesiredCapabilities();
cap.setCapability("deviceName", "Remi 3");
cap.setCapability("udid", "claeae297d72");
cap.setCapability("platformVersion", "5.1.1");
cap.setCapability("platformName", "Android");
cap.setCapability("appPackage", "io.selendroid.testapp");
cap.setCapability("appActivity", ".HomeScreenActivity");
cap.setCapability("unicodeKeyboard", "True");
cap.setCapability("noSign", "True");
driver = new AndroidDriver<>(new URL("http://127.0.0.1:4723/wd/hub"), cap);
}
@AfterClass
public void endTest(){
log.info("-----测试结束-----");
driver.quit();
}
public AndroidDriver<AndroidElement> getDriver(){
return driver;
}
}