Appium Appium 自动化测试演示

GUO · 2013年11月15日 · 最后由 聿小翼 回复于 2015年12月02日 · 3312 次阅读
本帖已被设为精华帖!

经验就是:
每两次操作之间,一定要间隔几秒,不然可能会由于加载不完全,导致操作异常
初始隐藏的控件,要判断状态,等显示后再操作

最后输出:
测试截图的相似度百分比(之前某一次结果),用来做正确性验证,基本上相似度小余 90% 的都有点问题

密码:testerhome.com

PS:
原来不支持嵌入视频啊
相似度计算是看的 http://blog.csdn.net/gzlaiyonghao/article/details/2325027

现在搞不太清楚的就是怎么判断当前的位置,是在什么窗口下面,和如何打印出当前位置的所有控件

贴下代码

# coding:utf8
import logging
import histsimilar
import traceback
import time
import os
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException, WebDriverException

logging.basicConfig(filename='log.txt',level=logging.INFO)
success = True
desired_caps = dict()
desired_caps['browserName'] = 'iOS'
desired_caps['platform'] = 'Mac'
desired_caps['version'] = '6.1'
desired_caps['app'] = os.path.abspath('测试app的位置')

#def is_alert_present(wd):
#    try:
#        wd.switch_to_alert().text
#        return True
#    except:
#        return False



def click(element):
    element.click()
    time.sleep(3)
    logging.info(str(c) + ' time click ' + wd.page_source)
    screenshot()


def send_keys(element, keys):
    element.send_keys(keys)
    time.sleep(3)
    logging.info(str(c) + ' time click ' + wd.page_source)
    screenshot()


def execute_script(script, args):
    wd.execute_script(script, args)
    time.sleep(3)
    screenshot()


def mk_new_dir():
    global path, base
    base = '截图存放的位置'
    if not os.path.isdir(base):
        path = base + '0/'
        os.mkdir(path)
        return
    path = base + str(len(os.listdir(base))) + '/'


c = 0


def screenshot():
    global c, path
    if not os.path.isdir(path):
        os.mkdir(path)
    wd.get_screenshot_as_file(path + str(c) + '.png')
    c += 1


def compare():
    dirs = list()
    for dirname in os.listdir(base):
        if os.path.isdir(base + dirname):
            dirs.append((os.path.getmtime(base + dirname), dirname))
    if len(dirs) < 3:
        return
    dirs.sort()
    new = dirs[len(dirs) - 1][1]
    previous = dirs[len(dirs) - 2][1]
    names = [(os.path.getmtime(base + new + '/' + filename), filename) for filename in
             os.listdir(base + new)]
    names.sort()
    for n in names:
        if n[1].startswith('.'):
            continue
        if os.path.exists(base + previous + '/' + n[1]):
            print n[1] + ' similarity : ' + str(
                int(histsimilar.calc_similar_by_path(base + new + '/' + n[1],
                                                     base + previous + '/' + n[1]) * 100)) + '%'


try:
    mk_new_dir()
    wd = webdriver.Remote('http://0.0.0.0:4723/wd/hub', desired_caps)
    wd.implicitly_wait(5)
    try:
        button = wd.find_element_by_name('点击开启')
        while not button.is_displayed():
            #screenshot()
            time.sleep(1)
        time.sleep(3)
        click(button)
    except WebDriverException, NoSuchElementException:
        logging.info('没有开机动画')
    click(wd.find_element_by_xpath("//window[1]/tableview[1]/cell[1]/button[1]"))
    click(wd.find_element_by_name(" 返回"))
    click(wd.find_element_by_xpath("//window[1]/tableview[1]/cell[1]/button[2]"))
    click(wd.find_element_by_name(" 返回"))
    click(wd.find_element_by_name("游记"))
    click(wd.find_element_by_name("qunar login"))
    send_keys(wd.find_element_by_xpath("//window[1]/image[2]/textfield[1]"), "lvtu@yopmail.com")
    send_keys(wd.find_element_by_xpath("//window[1]/image[3]/secure[1]"), "lvtu@yopmail.com")
    click(wd.find_element_by_name("登录"))
    try:
        click(wd.find_element_by_xpath("//window[1]/button[3]"))
    except:
        logging.info('没上传提示')
    click(wd.find_element_by_xpath("//window[1]/tableview[1]/cell[1]/text[1]"))
    click(wd.find_element_by_xpath("//window[1]/tableview[1]/cell[1]"))
    execute_script("mobile: swipe",
                   {"touchCount": 1, "startX": 157, "startY": 529, "endX": 156, "endY": 102, "duration": 0.5})
    click(wd.find_element_by_name(" 返回"))
    click(wd.find_element_by_name(" 返回"))
    click(wd.find_element_by_xpath("//window[1]/button[1]"))
except:
    traceback.print_exc()
    success = False
finally:
    if success:
        #raise Exception("Test failed.")
        compare()
        wd.quit()


计算图片相似度部分

# -*- coding: utf-8 -*-

import Image


def make_regalur_image(img, size=(256, 256)):
    return img.resize(size).convert('RGB')


def split_image(img, part_size=(64, 64)):
    w, h = img.size
    pw, ph = part_size

    assert w % pw == h % ph == 0

    return [img.crop((i, j, i + pw, j + ph)).copy() \
            for i in xrange(0, w, pw) \
            for j in xrange(0, h, ph)]


def hist_similar(lh, rh):
    assert len(lh) == len(rh)
    return sum(1 - (0 if l == r else float(abs(l - r)) / max(l, r)) for l, r in zip(lh, rh)) / len(lh)


def calc_similar(li, ri):
#   return hist_similar(li.histogram(), ri.histogram())
    return sum(hist_similar(l.histogram(), r.histogram()) for l, r in zip(split_image(li), split_image(ri))) / 16.0


def calc_similar_by_path(lf, rf):
    li, ri = make_regalur_image(Image.open(lf)), make_regalur_image(Image.open(rf))
    return calc_similar(li, ri)


def make_doc_data(lf, rf):
    li, ri = make_regalur_image(Image.open(lf)), make_regalur_image(Image.open(rf))
    li.save(lf + '_regalur.png')
    ri.save(rf + '_regalur.png')
    fd = open('stat.csv', 'w')
    fd.write('\n'.join(l + ',' + r for l, r in zip(map(str, li.histogram()), map(str, ri.histogram()))))
    #   print >>fd, '\n'
    #   fd.write(','.join(map(str, ri.histogram())))
    fd.close()
    import ImageDraw

    li = li.convert('RGB')
    draw = ImageDraw.Draw(li)
    for i in xrange(0, 256, 64):
        draw.line((0, i, 256, i), fill='#ff0000')
        draw.line((i, 0, i, 256), fill='#ff0000')
    li.save(lf + '_lines.png')

共收到 44 条回复 时间 点赞

视频嵌入 应该可以做。

滑动 Splash 后的产品介绍页面是如何做到的?

我觉得这个问题在于怎么做很多的断言,关于操作不能看出来任何的优势啊

#2 楼 @xiaomayi0323 是指滑动怎么做到的么?

#5 楼 @xiaomayi0323 比如 python 的这个例子就可以实现。

js_snippet = "mobile: swipe"
args = {'startX':0.5, 'startY':0.2, 'startX':0.5, 'startY':0.95, 'tapCount':1, 'duration':10}
driver.execute_script(js_snippet, args)
GUO #7 · 2013年11月15日 Author

#3 楼 @monkey 没做断言,主要依靠最后的截图相似度来判断的,感觉写断言工作量有点大
有其他什么好办法没?

GUO #9 · 2013年11月15日 Author

@seveniruby @lihuazhang @monkey 打印当前的所有控件,有什么接口没?Inspector 上能打印所有的控件,按说是应该有接口。

#9 楼 @guo 可以先打印下 page_source 看看,不过打印出的格式比较乱
以 Python 为例
print driver.page_source

#6 楼 @monkey 谢谢,等一下试试

#9 楼 @guo 我也在研究 appium 的定位和元素枚举, 他们这块实现的不是太好. 得需要我们封装下.
对比截图是一种方法, 还可以直接对比 page_source, 这两个方法都可以使用. 他们对比可以发现不同层面的问题.

等待的问题, 我建议不要 sleep, 而是使用直接的元素定位, 每次操作元素前都尽量重新定位, 而不是使用之前定义好的一些元素. 操作前定位就可以让系统自己去等待元素的出现, 而不是强行的 sleep.

打印控件这种东西, 就算 appium 有接口, 也不要使用, 尽量通过 webdriver 协议来实现. 对于 appium 还没有完全实现的 webdriver 接口, 可以自己尝试封装出来.

@guo 这个视频太棒了。 自动化脚本 和 测试 app 能 share 么?

GUO #14 · 2013年11月15日 Author

#12 楼 @seveniruby 如果操作前不 sleep 的话,我这会出现 WebDriverException: Message: u'An unknown server-side error occurred while processing the command.' 的错误。可能和 app 的过度动画效果有关系。
page_source 还真能看所有控件,真全,连几格信哈都有
另外问下,有没有碰到 UIAApplication 的中文名显示乱码的问题,其他都没事,就这个东西是乱码的

GUO #15 · 2013年11月15日 Author

#13 楼 @lihuazhang 脚本行,很简单,等整理整理的。
app 不行,你懂的

#14 楼 @guo 你脚本语言用的是什么,如果是 Python,在第一行加上'# coding=utf-8'试试

#6 楼 @monkey 我的滑动还是没有效果,Appium 报下面的信息:
Responding to client with success: {"status":0,"value":null,"sessionId":"a4f0725f-737f-47f1-b2c8-c14213f7283b"}
我的代码片段:

js_snippet = "mobile: swipe"
args = {'endX':0.95, 'endY':0.5, 'touchCount':1, 'duration':1.8}
self.driver.execute_script(js_snippet, args)

#15 楼 @guo 分享一下你的脚本吧,让大家大家学习学习

GUO #19 · 2013年11月15日 Author

#17 楼 @xiaomayi0323 utf8 这行加了,乱码是 app 安装到模拟器之后显示的名字是乱码的,用 xcode 就没事。
那个滑动,可以这样,用 inspector 录一下,这样位置代码都有了,我就这么搞的。

#19 楼 @guo 哦,我也碰到了这个问题,应用的名字是乱码的;关键是我的 inspector Luanch 不起来,你的那个产品介绍页面是从左向右滑动的吗,能否把你滑动的那部分代码让我看看?

GUO #21 · 2013年11月15日 Author

#20 楼 @xiaomayi0323 就最后查看照片详细的时候是从下向上滑动的,开始的引导页是自动滚动的

#21 楼 @guo 你们开始的引导页是自动滚动的?本身程序就是这样的吗?我们的必须要手动滑三次

GUO #23 · 2013年11月15日 Author

#22 楼 @xiaomayi0323 对,不过这都不是重点,前一个版本也是划的

同意~求分享 code~~

哈哈原来已经有了呀~~~感谢感谢

对了,关于图片的,我之前也有一些代码。也是用 PIL 库来实现的~~待会儿我也来贴下~~

GUO #28 · 2013年11月15日 Author

#27 楼 @xiaomayi0323 不过我的环境就是 xcode5 和 iOS7

#28 楼 @guo 晕,这是怎么个回事

GUO #30 · 2013年11月15日 Author

#29 楼 @xiaomayi0323 刚升级 iOS7 那会是这样,不能滑动,后续就修复了

GUO #31 · 2013年11月18日 Author

#29 楼 @xiaomayi0323 好像我搞错了,不能滑动的问题还是存在

#31 楼 @guo 哦,那你现在的滑动是这么实现的?

#12 楼 @seveniruby 不用 sleep,每次操作前怎么重新定位元素,能举例一下吗?菜鸟不懂哈

#33 楼 @luyi0824 就是使用 find_element 后直接操作, 最好是使用 page object 模式

GUO #35 · 2013年12月11日 Author

#34 楼 @seveniruby 我 sleep 是在两次 find 之间,操作的话还是 find 后直接操作的,page object 是什么意思?不懂

#35 楼 @guo page object 模式可以参考下 selenium 的文档,

send_keys 这块要是传值汉字 怎么处理呢,我看都是传值的英文

GUO #38 · 2013年12月18日 Author

#37 楼 @oscar 没试过,好像看到过有人说有问题

如果加断言怎么加?
在 try 段中,直接使用 assertEqual() 出现报错。

#39 楼 @532589730
同问 断言怎么加呢?

#38 楼 @guo 我想问下滑动目前实现没?我就在解决引导页怎么滑动的问题,头疼死了

视频需要密码哎

好贴,学习了。

如果需要连续滑动两次,怎样写会比较好呀??看了各位的代码,我觉得我写的很繁琐。

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