Appium [干货] 关于 Appium 的一点分享

testly · 2017年05月02日 · 最后由 testly 回复于 2017年06月01日 · 5313 次阅读
本帖已被设为精华帖!

Appium 日常干货分享

最新的 io.appium java -client 升级到 5.0 了 ,有些 方法没来得及更新。

Capabilities 配置(Android):

public void StartUp() throws InterruptedException,  IOException {
        DesiredCapabilities capabilities = new DesiredCapabilities(); 
        capabilities.setCapability("device","Android");
        capabilities.setCapability("deviceName", "192.168.57.101:5555");
        capabilities.setCapability("udid", "192.168.57.101:5555");
        capabilities.setCapability("platformVersion", "6.0");
        capabilities.setCapability("appPackage", "com.sds.android.ttpod");
        capabilities.setCapability("appActivity", "com.ali.music.entertainment.splash.SplashActivity");
        //capabilities.setCapability(MobileCapabilityType.APP, "链接地址");//引用apk下载地址
        capabilities.setCapability("unicodeKeyboard", "True");
        capabilities.setCapability("resetKeyboard", "True");
        capabilities.setCapability("autoAcceptAlerts", true);// 自动接受提示信息
        driver = new AndroidDriver<>(new URL("http://"+ip+":"+port+"/wd/hub"), capabilities);
        driver.manage().timeouts().implicitlyWait(40, TimeUnit.SECONDS);

    } 

Capabilities 配置(IOS):


public void SetIosDriverSession(Capabilities cap) throws MalformedURLException, InterruptedException {
    DesiredCapabilities capabilities = new DesiredCapabilities();
    capabilities.setCapability(CapabilityType.BROWSER_NAME, "");
    capabilities.setCapability("platformName", "iOS");
    capabilities.setCapability("newCommandTimeout", "30000");  
    capabilities.setCapability("deviceName", cap.getDriverkey());   
    capabilities.setCapability("platformVersion", cap.getDriverVersion());
    capabilities.setCapability("app", "/Users/liyu/Documents/TTEntertainment-iOS.app");
    capabilities.setCapability("autoAcceptAlerts", "True");
    capabilities.setCapability("unicodeKeyboard", "True");  
    capabilities.setCapability("resetKeyboard", "True"); 
    try {

            driver= new IOSDriver(new URL("http://"+cap.getIp()+":"+cap.getProt()+"/wd/hub"), capabilities);
            driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
    } catch (MalformedURLException e) {
        e.printStackTrace();
    }
            driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);  
    Thread.sleep(10000);
}

UiSelector text

driver.findElementByAndroidUIAutomator("new UiSelector().text(\"Custom View"\")").click();

UiSelector textContains 全局文本匹配

driver.findElementByAndroidUIAutomator("new UiSelector().textContains(\"发现\")").click();

UiSelector textMatches

driver.findElementByAndroidUIAutomator("new UiSelector().textMatches(\"^Custom.*\")").click();

UiSelector textStartsWith

driver.findElementByAndroidUIAutomator("new UiSelector().textStartsWith(\"Custom\")").click();

检查网络

/***
* 检查网络
* @return 是否正常
*/
public static boolean checkNet(){
String text=driver.getNetworkConnection().toString();
if(text.contains("Data: true"))
return true;
else
return false;
}

desc 的 view

/***
* 根据UIautomator底层方法得到对应desc的view
* @param desc名
* @return View
*/
public static WebElement getViewbyUidesc(String name){
return driver.findElementByAndroidUIAutomator("new UiSelector().descriptionContains(\""+name+"\")");
}

得到对应 text 的 view

/***
* 根据UIautomator底层方法得到对应text的view
* @param text名
* @return View
*/
public static WebElement getViewbyUitext(String name){
return driver.findElementByAndroidUIAutomator("new UiSelector().textContains(\""+name+"\")");
}

超时时间:

driver.manage().timeouts().implicitlyWait(10,TimeUnit.SECONDS);

打印整个页面 PageSource:

System.out.print(driver.getPageSource());

获取当前时间并截图,命名:


public static String getScreen(){
String fileRoute="路径";
SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmm");
String picname=fileRoute+df.format(new Date()).toString()+".png";
       File screen = driver.getScreenshotAs(OutputType.FILE);
       System.out.println(picname);
       File screenFile = new File(picname);
       try {
           FileUtils.copyFile(screen, screenFile); 
           String time=df.format(new Date()).toString();
           System.out.println("当前时间"+time);
           return time;
       } catch (IOException e) {
           e.printStackTrace();
       }
return null;

}

APP 内上滑:

driver.swipe(250, 300, 250, 1400, 0); 

APP 内上滑:

driver.swipe(250,1400, 250,300 , 0);
//driver.navigate().forward(); // 前进
//driver.navigate().back(); // 后退
driver.navigate().refresh(); // 刷新``

切换 WEBVIEW

/***
* 切换WEB页面查找元素
*/
public static void switchtoWeb(){
try {
     Set<String> contextNames = driver.getContextHandles();
     for (String contextName : contextNames) {
       // 用于返回被测app是NATIVE_APP还是WEBVIEW,如果两者都有就是混合型App
       if(contextName.contains("WEBVIEW")||contextName.contains("webview")){
       driver.context(contextName);
       System.out.println("跳转到web页 开始操作web页面"); 
       }
     }
}catch (Exception e) {
     e.printStackTrace();
}
}

滑动相关

/***
* 上滑1/4屏幕
*/
public static void slideUP(){
int x=driver.manage().window().getSize().width;
int y=driver.manage().window().getSize().height;
driver.swipe(x/2, y/3*2, x/2, y/3*1, 0);
}
/***
* 下滑1/4屏幕
*/
public static void slideDown(){
int x=driver.manage().window().getSize().width;
int y=driver.manage().window().getSize().height;
driver.swipe(x/2, y/3*1, x/2, y/3*2, 0);
}
/***
* 左滑1/2屏幕
*/
public static void slideLeft(){
int x=driver.manage().window().getSize().width;
int y=driver.manage().window().getSize().height;
driver.swipe(x/4*3, y/2, x/4*1, y/2, 0);
}
/***
* 右滑1/2屏幕
*/
public static void slideRight(){
int x=driver.manage().window().getSize().width;
int y=driver.manage().window().getSize().height;
driver.swipe(x/4*1, y/2, x/4*3, y/2, 0);
}
/***
* 特殊上滑
* @param 传入从左到右宽度的百分比(1-99之间)
*/
public static void slideUP(int i){
Assert.assertFalse("上滑宽度传入错误", i<=0||i>=100);
int x=driver.manage().window().getSize().width;
int y=driver.manage().window().getSize().height;
driver.swipe(x/10*i, y/3*2, x/10*i, y/3*1, 0);
}
/***
* 特殊下滑
* @param 传入从左到右宽度的百分比(1-99之间)
*/
public static void slideDown(int i){
Assert.assertFalse("下滑宽度传入错误", i<=0||i>=100);
int x=driver.manage().window().getSize().width;
int y=driver.manage().window().getSize().height;
driver.swipe(x/10*i, y/3*1, x/10*i, y/3*2, 0);
}
/***
* 特殊左滑
* @param 传入从上到下宽度的百分比(1-99之间)
*/
public static void slideLeft(int i){
Assert.assertFalse("左滑宽度传入错误", i<=0||i>=100);
int x=driver.manage().window().getSize().width;
int y=driver.manage().window().getSize().height;
driver.swipe(x/4*3, y/10*i, x/4*2, y/10*i, 0);
}
/***
* 特殊右滑
* @param 传入从上到下宽度的百分比(1-99之间)
*/
public static void slideRight(int i){
Assert.assertFalse("左滑宽度传入错误", i<=0||i>=100);
int x=driver.manage().window().getSize().width;
int y=driver.manage().window().getSize().height;
driver.swipe(x/4*2, y/10*i, x/4*3, y/10*i, 0);
}

content-desc 定位

/***
* 根据content-desc查找元素
* @param view的类型
* @param content-desc 的内容
* @return
*/
public static WebElement getViewbyXathwithcontentdesc(String view,String name){
return driver.findElementByXPath("//"+view+"[contains(@content-desc,'"+name+"')]");
}

根据 xpath text 查找元素

/***
* 根据text查找元素
* @param view的类型
* @param text的内容
* @return
*/
public static WebElement getViewbyXathwithtext(String view,String name){
return driver.findElementByXPath("//"+view+"[contains(@text,'"+name+"')]");
}

### 截图 文件名: 年月日时分秒
``` java
/***
* 截图 文件名: 年月日时分秒
*/
public static String getScreen(){
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
String picname=finalElement.phoneScreens+df.format(new Date()).toString()+".png";
//picname=picname.replaceAll(":", "-");
//picname=picname.replaceAll(" ", "-");
        File screen = driver.getScreenshotAs(OutputType.FILE);
        System.out.println(picname);
        File screenFile = new File(picname);
        try {
            FileUtils.copyFile(screen, screenFile); 
        } catch (IOException e) {
            e.printStackTrace();
        }
return picname;
}

截图

/***
* 截图 文件名: 内容-年月日时分秒
*/

public static String getScreen(String name){
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
String picname=finalElement.phoneScreens+name+df.format(new Date()).toString()+".png";
        File screen = driver.getScreenshotAs(OutputType.FILE);
        System.out.println(picname);
        File screenFile = new File(picname);
        try {
            FileUtils.copyFile(screen, screenFile); 
        } catch (IOException e) {
            e.printStackTrace();
        }
        return picname;
}

绝对坐标 传入长宽的像素点

/***
* 绝对坐标 传入长宽的像素点
* @param 宽度从左到右的像素点
* @param 长度从上到下的像素点
*/
public static void clickScreen(int i,int j){
int x=driver.manage().window().getSize().width;
int y=driver.manage().window().getSize().height;
driver.tap(1, i, j, 200);
}

相对坐标 传入长宽的百分比

/***
* 相对坐标 传入长宽的百分比
* @param 宽度从左到右的百分比
* @param 长度从上到下的百分比
*/
public static void clickScreen100(int i,int j){
int x=driver.manage().window().getSize().width;
int y=driver.manage().window().getSize().height;
driver.tap(1, x*i/100, y*j/100, 200);
}

预留示例

/***
* log记录
* @param 图片保存路径
* @param Exception参数
* @param AssertionError参数
* @param 测试用例名
*/
public static void getlog(String text, Exception error, AssertionError assertError, String testname){
SimpleDateFormat df = new SimpleDateFormat("MM-dd-HH-mm");
System.out.println("当前时间"+df.format(new Date()));
String filename=finalElement.errorfile+testname+"-"+df.format(new Date()).toString()+".txt";
File file=new File(finalElement.errorfile);
if(!file.exists())
file.mkdirs();
try {
   File f = new File(filename);
   if (!f.exists()) 
   f.createNewFile();
FileWriter fw = new FileWriter(f, true);
PrintWriter pw = new PrintWriter(fw);
pw.append(testname+" 测试failed\r\n");
pw.append("截图保存为:"+text+"\r\n");
try{
pw.append("eclipse报错为:\n"+error.toString()+"\r\n");
error.printStackTrace(pw);
} catch (Exception e){}
try{
pw.append("断言报错为:"+assertError.toString()+"\r\n");
assertError.printStackTrace(pw);
} catch (Exception e){}  
pw.flush();
pw.close();
file=new File(finalElement.errorlog);
if(!file.exists())
file.mkdirs();
String cmd="cmd /c \"adb logcat -d  *:E *:S |grep \"com.yiguo.app\" >"+finalElement.errorlog+testname+"-"+df.format(new Date()).toString()+".txt\"";
//System.out.println(cmd);
Runtime.getRuntime().exec(cmd);
} catch (Exception e) {
e.printStackTrace();
}
}

发送邮件:HTML/TXT(要把图片放到 IIS 路径拼接 url)

public void mail(String Value String subject ) {
String smtpHost ="172.17.1.23";
String from = "mail";
String to = "mail";
String subject = value2; //subject javamail自动转码
StringBuffer theMessage = new StringBuffer();
theMessage.append("<h2><font color=red>**截图:</font></h2>");
theMessage.append("<hr>");
theMessage.append(Value);
try {
Mail.sendMessage(smtpHost, from, to, subject, theMessage.toString());
}
catch (javax.mail.MessagingException exc) {
exc.printStackTrace();
}
catch (java.io.UnsupportedEncodingException exc) {
exc.printStackTrace();
}
}

``` java
public static void sendMessage(String smtpHost,String from, String to, String subject, String messageText)throws MessagingException,java.io.UnsupportedEncodingException
{
// Step  :  Configure the mail session
System.out.println("Configuring mail session for: " + smtpHost);
java.util.Properties props = new java.util.Properties();
props.setProperty("mail.smtp.auth", "true");//指定是否需要SMTP验证
props.setProperty("mail.smtp.host", smtpHost);//指定SMTP服务器
props.put("mail.transport.protocol", "smtp");
Session mailSession = Session.getDefaultInstance(props);
mailSession.setDebug(true);//是否在控制台显示debug信息
// Step  :  Construct the message
System.out.println("Constructing message -  from=" + from + "  to=" + to);
InternetAddress fromAddress = new InternetAddress(from);
InternetAddress toAddress = new InternetAddress(to);
MimeMessage testMessage = new MimeMessage(mailSession);
testMessage.setFrom(fromAddress);
testMessage.addRecipient(javax.mail.Message.RecipientType.TO, toAddress);
testMessage.setSentDate(new java.util.Date());
testMessage.setSubject(MimeUtility.encodeText(subject,"gb2312","B"));
testMessage.setContent(messageText, "text/html;charset=gb2312");
System.out.println("Message constructed");
// Step  :  Now send the message
Transport transport = mailSession.getTransport("smtp");
transport.connect(smtpHost, "mail", "passwd");
transport.sendMessage(testMessage, testMessage.getAllRecipients());
transport.close();
System.out.println("Message sent!");
}

欢迎一起交流,一起进步 可以关注我的微信公众号:“测试开发进阶” - 点我关注

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 23 条回复 时间 点赞

楼主 你那个 demand monkey 的帖子怎么删了、

Addison 回复

被暗坑了~~~

testly 回复

newdream

wangpengfei100 回复

懂得

思寒_seveniruby 将本帖设为了精华贴 05月03日 20:03
  1. capabilities.setCapability("autoAcceptAlerts", true);//这行是自动点掉手机上面的弹窗的意思吗?

  2. 看楼主这个还没切到 ui2.0,切到 ui2.0 后,自动点掉弹窗有什么好的方法吗

bauul 回复

1,capabilities.setCapability("autoAcceptAlerts", true);// 全新安装时点掉一些系统弹框
2,自动点掉系统弹框么?

testly 回复

对,自动点掉系统弹框,分两种情况

  1. ui2.0 server 起来之前
  2. ui2.0 server 起来之后
bauul 回复

这个情况除了 6.0 我好像并没遇到过,

testly 回复

没碰到的原因是什么?测试时,你关闭了手机端的"通过 USB 验证应用 “这个功能吗?可是这个功能是灰色的,关不了啊,我的是 6.0 的手机

testly #11 · 2017年05月08日 Author
bauul 回复

“” 过 USB 验证应用
“” 这个并不是每次都出现呀。

testly 回复

出现时,你怎么解决的呢

testly #13 · 2017年05月08日 Author
bauul 回复

目前 capabilities.setCapability("autoAcceptAlerts", true);// 自动接受提示信息
这个已经满足我现在的需求,你说的这个弹框我印象中手工确认不会再出现,所以没有必要去自动化来做~!

14楼 已删除

finalElement 类是什么?

testly #16 · 2017年05月15日 Author
fastcrosss 回复

私有类

17楼 已删除

@testly 请问,我安装的 appium 是 1.6.4 的版本,怎么用 Python 脚本启动 appium server,打开终端输入 appium 提示:-bash: appium: command not found

testly #19 · 2017年05月25日 Author
lei 回复

你装的是 客户端吧?
先学者使用 appium doctor 检查

请问 Appium,mac 环境下命令行安装后,是否还需要安装桌面版?另外始终不解,命令行安装 appium 为什么需要 java,(我的理解:单纯安装 apium 后,然后使用 ruby、python 脚本语言去编写测试用例,所以现在有些傻傻分不清,这个环境在 mac 端到底怎么配置了),目前已经安装好 appiu,执行 appiu-doctor 没有报错

testly #21 · 2017年05月26日 Author
阿gu 回复

命令安装和 客户端是一个性质的。
用到 java 的主要原因是 appium 内部通讯是主要底层交互是 Android 的 UiAutomator
可以先去了解一下大概的原理

testly 回复

谢谢,如果命令行安装好后,有没有直接的后面的操作方法,比如我需要验证一个基本用例查看效果,看了蛮多帖子,还没有发现更好的或者可以理解的,不知道你这边是否有好的推荐?

testly #23 · 2017年05月26日 Author
阿gu 回复

网上例子一大把,你先仔细看看,碰到问题自己思考一下,查一下~!

testly 回复

😅 非常感谢 u 如果搞不懂,还会来请教

25楼 已删除
testly 回复

xcode-select: error: tool 'xcodebuild' requires Xcode, but active developer directory '/Library/Developer/CommandLineTools' is a command line tools instance 运行后报这个错 是什么因为呢?没度娘到原因,而且 appium-doctor 显示已经安装了 怎么会没找到 xcode 呢

testly #27 · 2017年06月01日 Author
阿gu 回复

报错 已经告诉你了!

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册