Appium appium 自动化分享和请教

隔壁老王 · 2015年08月15日 · 最后由 JennyHui 回复于 2015年08月18日 · 1313 次阅读

前提

最近在学习 appium,一直想做一个自动化平台,达到数据驱动,测试案例等这些数据的分离。打算分三步来学习:

熟悉 appium 的基本使用方法,可以编写简单自动化脚本

appium 的数据分离。比如用 excel 管理数据驱动

appium 的自动化平台,所有数据存放在数据库
好了废话少说,我直接上代码:

testData 数据准备

driver.ini
[DEFAULT]
devices=SKMFFQPFYSEYEULZ
platformName=android
platformVersion=4.1
appPackage=com.tencent.mm
appActivity=.ui.LauncherUI
Remote=http://localhost:4723/wd/hub

BaseGetdriver.py
#获取app的信息。设备名,测试的app的包名,appium 服务器的信息
import configparser
from testData.getDir import get_driver_ini
class getDriver():
    def __init__(self):
        config = configparser.ConfigParser()
        # config.read('d:\driver.ini'))
        config.read(get_driver_ini('driver.ini'))
        self.devices = config['DEFAULT']['devices']
        self.platformName = config["DEFAULT"]['platformName']
        self.platformVersion = config['DEFAULT']['platformVersion']
        self.appPackage = config['DEFAULT']['appPackage']
        self.appActivity = config['DEFAULT']['appActivity']
        self.Remote = config['DEFAULT']['Remote']
    def get_plaetformName(self):
        print(self.platformName)
        return self.platformName
        ......
gobal.py 扩展
from testData.BaseGetdriver import *
# 查找元素的方式
class findElemtType():
    FINDNAME = "name"
    FINDID = "id"
    XPATH = "xpath"

# 操作元素的类型
class operateType():
    CLICK = "click"

# 为了后面的扩展
class devicesinfo():
    gd = getDriver()
    platformVersion = gd.get_platformVersion()
    devices = gd.get_devices()
    appActivity = gd.get_appActivity()
    remote = gd.get_Remote()
    appPackage = gd.get_appPackage()
    plaetformName = gd.get_plaetformName()

BaseOperateElement.py 操作界面元素
# 此脚本主要用于查找元素是否存在,操作页面元素
# cts 传入的driver
# operate_type 操作类型。比如点击,下拉,拖动等等,
# element_type 格式:(查找类型,查找的具体内容)-》(id,name,xpath)
# element_info 是具体的元素 ("通讯录",com.android.calculator2:id/digit6",/android.widget.TextView[contains(@text,'Add note'

class getOperateElement():
    def __init__(self, driver="", element_type="", element_info="", operate_type=None):
        self.operate_type = operate_type
        self.cts = driver
        self.element_type = element_type
        self.element_info = element_info
    def findElement(self):
        try:
            if self.element_type == findElemtType.FINDNAME:
                WebDriverWait(self.cts.driver, 10).until(lambda x: x.find_element_by_name(self.element_info))
                return True
            if self.element_type == findElemtType.FINDID:
                WebDriverWait(self.cts.driver, 10).until(lambda x: x.find_element_by_id(self.element_info))
                return True
            if self.element_type == findElemtType.XPATH:
                WebDriverWait(self.cts.driver, 10).until(lambda x:x.find_element_by_xpath(self.element_info))
                return True
        except selenium.common.exceptions.TimeoutException:
            return False
    def operateElement(self):
        if self.element_type == findElemtType.FINDID:
            elements_index_operate_id(self.operate_type, self.cts, self.element_info)
        if self.element_type == findElemtType.FINDNAME:
            elements_index_operate_name(self.operate_type, self.cts, self.element_info)
        if self.element_type == findElemtType.XPATH:
            elements_index_operate_xpath(self.operate_type, self.cts, self.element_info)

#元素属性为id的操作
def elements_index_operate_id(index, cts, element_info):
    elements = {
        operateType.CLICK:lambda:cts.driver.find_element_by_id(element_info).click()
    }
    return elements[index]()
#元素为name属性的操作
def elements_index_operate_name(index, cts, element_info):
    elements = {
        operateType.CLICK:lambda:cts.driver.find_element_by_name(element_info).click()
    }
    return elements[index]()

def elements_index_operate_xpath(index, cts, element_info):
    print(element_info)
    elements = {
        operateType.CLICK:lambda:cts.driver.find_element_by_xpath("//android.widget.ListView/android.widget.LinearLayout[@index='7']").click()
    }
    return elements[index]()

testCase

BaseTestCase.py
class getBaseTestCase(object):
# 测试案例的基类
# operate_type 操作类型。比如点击,下拉,拖动等等,
# element_type (查找类型:name/id/xpah)
 # element_info(具体的详情:“/android.widget.TextView[contains(@text,'Add note'"))
    def __init__(self, element_type, element_info, operate_type):
        self.operate_type = operate_type
        self.element_types = element_type
        self.element_info = element_info
    def get_operate_type(self):
        return self.operate_type

    def get_element_types(self):
        return self.element_types

    def get_element_info(self):
        return self.element_info

testDriver 测试案例编写

weChatHome.py
from testCase.BaseTestCase import *
from testData.Global import *
def chatHomeClick():
   return getBaseTestCase(findElemtType.XPATH, "//android.widget.ListView/android.widget.LinearLayout[@index='7']", operateType.CLICK)

testRunner 测试运行

ch=chatHomeClick()
class ContactsAndroidTests(unittest.TestCase):
    def setUp(self):
        desired_caps = {}
        desired_caps['platformName'] = devicesinfo.plaetformName
        desired_caps['platformVersion'] = devicesinfo.platformVersion
        desired_caps['deviceName'] = devicesinfo.devices
        desired_caps['appPackage'] = devicesinfo.appPackage
        desired_caps['appActivity'] = devicesinfo.appActivity

        self.driver = webdriver.Remote(devicesinfo.remote, desired_caps)
    def tearDown(self):
        self.driver.close_app()
        self.driver.quit()
    def test_001(self):
        if getOperateElement(driver=self, element_type=ch.get_element_types(), element_info=ch.get_element_info()).findElement():
            getOperateElement(driver=self, element_type=ch.get_element_types(), element_info=ch.get_element_info(), operate_type=ch.get_operate_type()).operateElement()
        else:
           print("没有找到页面元素")
if __name__ == '__main__':
   suite = unittest.TestSuite()
    suite.addTest(ContactsAndroidTests("test_001"))
    unittest.TextTestRunner(verbosity=2).run(suite)

总结讨论

1.测试结果暂时还没有想好用什么,打算用 pyh 手动拼接 html 页面的方式。这样比较灵活,自定义样式
2.测试案例我这里真的没有想到好方法,RF 不支持 python 3,而我也是最近才开始学习 python3,代码能力不强。想引进 BDD 数据驱动的概念,不知道怎么入手?希望各位帮忙指点下测试案例这块要怎么管理?
3.以上都是我最近的学习总结和想法,希望大家多多指导,谢谢~~

共收到 8 条回复 时间 点赞

你最后那段应该是想表达 RF 不支持 3.0 吧,RF 咋可能不支持 2.7 呢

#1 楼 @qitaos 已经修改~

关于测试报告,我是用 testNG 写脚本,testNG 会自动产生结果 xml,我搭了一个 django 服务器,解析 xml 成 json,然后利用 django 的模板插入网页,另外可以增加一些其他的模块,比如错误日志,截图展示,性能数据,性能数据可以用 highcharts 做,这样比拼接要更灵活一点。缺点是不能脱离服务器。

您好,我在用 Appium 测试真机的时候出现这个问题 error: Failed to start an Appium session, err was: Error: android.util.AndroidException: INSTRUMENTATION_FAILED: com.baidu.searchbox.selendroid/io.selendroid.ServerInstrumentation
因为刚注册,没法发帖,所以在此处请教,非常感谢

#4 楼 @yy_u 看起来是 selendroid 的错误,但这么少的错误信息定位不了。建议你等到能发帖时把完整 log 贴上来吧。
如果等不及,建议参考 Appium 报错后查错指南 自己排一下错吧。

不错的实践。看你的写法应该是想把操作分离出来,然后再通过非代码的方式编写测试用例是吧?

数据驱动这个目前没做过,但看你目前的实现实际上应该是数据分离吧?我的对数据驱动的理解是 不仅仅是把输入的数据分离出来,还得把数据和业务逻辑绑定,通过数据自动选择业务逻辑。例如填写销售单,不同类型的销售单会需要会有不同的业务流和不同的数据,数据驱动需要做到仅给出销售单类型和对应数据即可执行测试。

我们目前主要使用的是关键字驱动,但同样提供了简单的数据分离的方式。对于环境配置相关的数据我们分离到了一个指定的文件中,以类似 ini 的方式记录。然后用例可通过一个特殊的关键字获取这个外部文件的指定数据,实现基本的外部参数配置。

对于测试结果,个人建议框架只需要支持把测试结果导出成 Junit 格式的 xml 文件,然后你就有一大堆现成工具可以利用这个 xml 文件用来生成各种不同的测试报告了,不用重复造轮子。

对于测试案例管理(我的理解是测试用例管理,不知道对不对?),我们目前是对 testlink 进行二次开发,添加了针对自动化测试用例编写的模块(把原有的用例编写模块改了下)以及导出测试用例的接口。因此具体用例管理可以继续和以前的手工用例一样通过 testlink 进行管理。因为用例管理这块看起来简单,实际上要做到能很好地进行团队协作还是需要做很多东西的(权限管理、不同项目之间的分离、测试计划的管理等),二次开发可以减少重复造轮子。

最后,如果不想在测试工具开发这边投入太多精力或者走太多弯路,最好还是先了解一下自己想做的功能目前有哪些优秀的框架有提供,学习他们的实现架构和实现方式,这样写起来速度会快很多。在 appium 用例封装这一块个人建议学习一下 robotframework 的 Selenium2Library 和 AppiumLibrary(在里面你会了解到元素查找应该怎么封装最为简便,同时这两个库对于 robotframework 的依赖也不强,主要依赖了几个工具类和日志模块,可以很方便地分离出来),以及 Page Object 设计模式。

#6 楼 @chenhengjie123 很感谢您的指导意见,让我受益匪浅,后面会参照您的思路去尝试下~

@shixue33 妹子的想法 想借助 django 不过具体的想法可能不一样 再说再说

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