Appium脚本和测试报告(Python)

我知道自学Appium和Python还是一个比较费时间的过程。所以,希望我的学习经历能带刚入坑的小伙伴少走一些弯路,尽快能用上这么牛逼的一个工具,足矣!

如何用Python语言写Appium测试脚本?我之前写过就是利用AppiumDesktop录制脚本,但是这个录制出来的脚本可读性比较差,今天我所讲到的就是修改录制出来的脚本,再模仿这个脚本去编写其他的测试用例。

今天讲到的测试App是我们公司正在开发的一款,使用的是iOS模拟器(当然真机也可以,配置一下真机的信息就好了,因为我们公司没有iPhone测试机,需要使用个人的手机进行测试,对于手机就比较爱惜了,主要还是因为穷,换个手机太难了。所以我就等在模拟器上验证以后再在真机上实践),实现的功能为登录。

以下为录制出来的脚本:

# This sample code uses the Appium python client
# pip install Appium-Python-Client
# Then you can paste this into a file and simply run with Python
from appium import webdriver

#**配置模拟器信息**
caps = {}
caps["platformName"] = "ios"
caps["deviceName"] = "iPhone 7 Plus"
caps["app"] = "/Users/guxuecheng/Desktop/SmartLife.app"
caps["platformVersion"] = "10.3"
caps["moReset"] = True
driver = webdriver.remote("[http://0.0.0.0:4723/wd/hub](http://0.0.0.0:4723/wd/hub)", caps)

/#**滑动屏幕**(第一次安装App的时候会有引导页)
TouchAction(driver)
.press({x: 335, y: 365})
.moveTo({x: -209: y: 2})
.release()

TouchAction(driver)
.press({x: 323, y: 387})
.moveTo({x: -213: y: 21})
.release()

#**点击“立即使用”按钮进入登录页面**
TouchAction(driver).tap([(212, 635)])
/#点击输入手机号textfield(这里是用xpath进行定位,但其实这种方法定位很不好,官方并不推荐,因为能否定位成功需要看人品,如果没有accessibility_id,最好还是和开发说一下,让万能的开发工程师在代码里给加上吧,我在后面修改好的脚本就是用的accessibility_id进行定位的)
el1 = driver.find_element_by_xpath("//XCUIElementTypeApplication[@name=\"i+智慧社区 \"]/XCUIElementTypeWindow[1]/XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeImage/XCUIElementTypeOther[2]/XCUIElementTypeOther/XCUIElementTypeOther[1]/XCUIElementTypeTextField")

el1.click()
el1.send_keys("12212345678")

#**点击输入密码textfield**
el2 = driver.find_element_by_xpath("//XCUIElementTypeApplication[@name="i+智慧社区"]/XCUIElementTypeWindow[1]/XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeImage/XCUIElementTypeOther[2]/XCUIElementTypeOther/XCUIElementTypeOther[2]/XCUIElementTypeSecureTextField")

el2.click()
el2.send_keys("6666666")

/#**点击登录按钮**
TouchAction(driver).tap([(217, 377)])

/#**登录以后立即退出**(网络稍有延迟,你都看不到首页就被退出了,所以这里我们会设置等待时间)
driver.quit()

看着上面这个脚本感觉咋样?你们有看着舒服的你们就这么搞,反正我看着不够清晰,然后就想办法对这个代码进行修改。

修改后的代码如下:

# This sample code uses the Appium python client
# pip install Appium-Python-Client
# Then you can paste this into a file and simply run with Python
# -*- coding: utf-8 -*-
#!/usr/bin/env python3
from appium import webdriver
import time
time.sleep(3)

caps = {}
caps["platformName"] = "ios"
caps["deviceName"] = "iPhone 7 Plus"
caps["app"] = "/Users/guxuecheng/Desktop/SmartLife.app"
caps["platformVersion"] = "10.3"
caps["moReset"] = True

driver = webdriver.Remote("http://0.0.0.0:4723/wd/hub", caps)

#滑动屏幕
driver.swipe ( 356, 598, -313, -6,3)
#滑动屏幕暂停1s,确保滑动到下一页
time.sleep(1)
driver.swipe ( 356, 598, -313, -6,3)
time.sleep(1)

#立即体验---进入登录页面
driver.tap([(212,635)])
time.sleep(1)

#注意这里使用的是accessibility_id进行定位
el1 = driver.find_element_by_accessibility_id("_phoneNumT")
el1.click()
el1.send_keys("12212345678")

el2 = driver.find_element_by_accessibility_id("_passwordT")
el2.click()
el2.send_keys("6666666")

#登录
driver.find_element_by_accessibility_id("_loginButton").click()

time.sleep(10)

driver.quit()

我个人还是觉得修改后的代码稍微美观一些。以上脚本所用到的接口都是Appium Python Client
如果你确实是一个小白(高手请绕道),在这里请思考一个问题:这样一个脚本在实际工作中能用吗?能满足测试要求吗?能替代手工吗?如果以上答案都为否定,那么该如何改进呢?
可爱的Python给我们提供了一个很好的框架 unittest.
利用这个框架我们就能写出更加完善的测试用例了。

下面就是用了unittest框架实现了登录功能的脚本

# This sample code uses the Appium python client
# pip install Appium-Python-Client
# Then you can paste this into a file and simply run with Python
# -*- coding: utf-8 -*-
#!/usr/bin/env python3
from appium import webdriver
#from time import sleep
import time
#from unittest import TestCase
import unittest
#import sys
#import os
time.sleep(3)

caps = {}
caps["platformName"] = "ios"
caps["deviceName"] = "iPhone 7 Plus"
caps["app"] = "/Users/guxuecheng/Desktop/SmartLife.app"
caps["platformVersion"] = "10.3"
caps["moReset"] = True

#TestCase类,所有测试用例类继承的基本类
class LoginTest(unittest.TestCase):
    
    #setUp()方法用于测试用例执行前的初始化工作。如测试用例中需要访问数据库,可以在setUp中建立数据库链接
    #并进行初始化。如测试用例需要启动Appium服务,则需要在该方法内启动Appium服务。
    def setUp(self):
        self.driver = webdriver.Remote("http://0.0.0.0:4723/wd/hub", caps)

    #tearDown()方法用于测试用例执行之后的善后工作。如关闭数据库连接,退出应用。
    #无论这个方法写在哪里,都是最后才执行
    def tearDown(self):
        self.driver.quit()

    #具体的测试用例,必须要以test开头
    def test_start(self):
        self.driver.swipe ( 356, 598, -313, -6,3)
        time.sleep(1)
        self.driver.swipe ( 356, 598, -313, -6,3)
        time.sleep(1)

        #立即体验---进入登录页面
        self.driver.tap([(212,635)])
        time.sleep(1)

        el1 = self.driver.find_element_by_accessibility_id("_phoneNumT")
        el1.click()
        el1.send_keys("13012345678")

        el2 = self.driver.find_element_by_accessibility_id("_passwordT")
        el2.click()
        el2.send_keys("6666666")

        #点击登录按钮
    self.driver.find_element_by_accessibility_id("_loginButton").click()
        time.sleep(3)
        self.assertIsNotNone(self.driver.find_element_by_accessibility_id('tab_button_home_normal'),['进入到首页,登录成功,TRUE'])
        time.sleep(10)

if __name__ == '__main__':

    #构造测试集  defaultTestLoader()即TestLoader()测试用例加载器,包括多个加载测试用例的方法,返回一个测试套件
    #loadTestsFromTestCase()根据给定的测试类,获取其中的所有测试方法,并返回一个测试套件
    suite = unittest.TestLoader().loadTestsFromTestCase(LoginTest)

    #unittest框架的TextTestRunner()类,通过该类下面的run()方法来运行suite所组装的测试用例,入参为suite测试套件
    unittest.TextTestRunner(verbosity=2).run(suite)
  
    #上面两行代码可以换成下面一行
    #unittest.main()

这段代码用了比较多的注释,所以看起来比较凌乱,主要是想让和我一样的小白尽快学一些东西,如果还有看不懂的可以留言讨论,互相学习。

再附加一个注册功能的脚本

这个脚本就是模仿登录脚本写的,主要是为接下来的内容做准备

# -*- coding: utf-8 -*-
#!/usr/bin/env python3

from appium import webdriver
import timeimport unittest

caps = {}
caps["platformName"] = "ios"
caps["deviceName"] = "iPhone 7 Plus"
caps["app"] = "/Users/guxuecheng/Desktop/SmartLife.app"
caps["platformVersion"] = "10.3"
caps["moReset"] = True

#TestCase类,所有测试用例类继承的基本类
class RegistTest(unittest.TestCase):
    
    def setUp(self):
        self.driver = webdriver.Remote("http://0.0.0.0:4723/wd/hub", caps)

    def tearDown(self):
        self.driver.quit()
    #具体的测试用例,必须要以test开头
    def test_start(self):
        #滑动屏幕
        self.driver.swipe ( 356, 598, -313, -6,3)
        time.sleep(1)
        self.driver.swipe ( 356, 598, -313, -6,3)
        time.sleep(1)

        #立即体验---进入登录页面
        el0 = self.driver.find_element_by_accessibility_id("useAppRightNow")
        el0.click()
        #点击注册按钮
        el1 = self.driver.find_element_by_accessibility_id("_registerButton")
        el1.click()
        #输入需要注册手机号
        el2 = self.driver.find_element_by_accessibility_id("_registerPhoneNumT")
        el2.click()
        el2.send_keys("12212345678")
        #输入验证码
        el3 = self.driver.find_element_by_accessibility_id("_registerSecurityT")
        el3.click()
        el3.send_keys("这里使用的是万能验证码")
        #输入密码
        el4 = self.driver.find_element_by_accessibility_id("_registerPasswordT")
        el4.click()
        el4.send_keys("6666666")
        #点击同意服务协议
        el5 = self.driver.find_element_by_accessibility_id("changeSwitch")
        el5.click()
        #点击注册按钮
        el6 = self.driver.find_element_by_accessibility_id("_registerbutton")
        el6.click()
        time.sleep(3)

        self.assertIsNotNone(self.driver.find_element_by_accessibility_id('住房信息'),['注册成功,TRUE'])
        time.sleep(10)

if __name__ == '__main__':

    suite = unittest.TestLoader().loadTestsFromTestCase(RegistTest)

    unittest.TextTestRunner(verbosity=2).run(suite)
    
    self.driver.quit()

上面已经给出两段脚本,我们如果分两次执行也没有问题,但是如果脚本达到200个呢?还这样一个个执行吗?显然是不行的。友善的Python也为我们解决了这样一个问题。她给我们提供了这个便利的同时也制定了一些规则,我们必须遵守这些规则才行。

合并脚本测试

规则:上面我们已经写了两个脚本,对于这两个py文件在命名上我们并没有什么限制,但是如果想要合并起来一并执行那就在命名上需要遵守规则了。这些文件的命名必须要以test开头。然后我们在新建一个py文件(这个文件命名就没有那个限制了),这些文件需要放到一个文件夹里才行,对于这个文件(这个文件我们暂且命名为test_suite.py)需要写的内容很少,代码如下:

import unittest

#test_register是文件名,RegistTest是该文件里定义的一个类
from test_register import RegistTest  
from test_login import LoginTest

if __name__ == '__main__':
    suite = unittest.TestSuite()
    suite.addTests(unittest.TestLoader().loadTestsFromTestCase(RegistTest))
    suite.addTests(unittest.TestLoader().loadTestsFromTestCase(LoginTest))
    
#这一步是在当前文件夹里自动生成一个测试报告,测试报告名称就叫:UnittestTextReport.txt.
   with open('./UnittestTextReport.txt', 'a') as f:
        runner = unittest.TextTestRunner(stream=f, verbosity=2)
       runner.run(suite)

大家看一下这个测试报告,不是很直观:

测试报告.png

但是这个测试报告又有他的好处,对于错误的用例,他会将错误的地方标注出来:

错误原因.png

这样呢也方便我们排查。就是有一点,不利于统计。下面贴一个更生动的测试报告截图,也是这两个用例的执行结果:

更直观的测试报告.png

这个测试报告怎么样?
测试报告生成的时间,多少用例执行通过,多少用例失败,一共有多少用例被执行,都一目了然。
这样一个直观的测试报告就是利用HTMLTestRunner.py这个文件来实现的,首先你需要先下载这样一个文件放在脚本所在的同一个文件夹里。然后修改你的test_suite.py文件,修改以后如下:

import unittest

from test_register import RegistTest
from test_login import LoginTest

import HTMLTestRunner


if __name__ == '__main__':
    suite = unittest.TestSuite()
    suite.addTests(unittest.TestLoader().loadTestsFromTestCase(RegistTest))
    suite.addTests(unittest.TestLoader().loadTestsFromTestCase(LoginTest))
    

    with open('HTMLReport.html', 'wb') as f:
        runner = HTMLTestRunner.HTMLTestRunner(stream=f,
                                title='MathFunc Test Report',
                                description='generated by HTMLTestRunner.',
                                verbosity=2
                                )
        runner.run(suite)

然后再执行修改后的脚本,就会在脚本所在的文件夹里自动生成一个html格式的测试报告,打开该报告就是如上所示。
你学会了吗?如果还有疑问欢迎留言讨论,互相学习,共同进步!

部分脚本的补充(解释):

1、 一个python的文件有两种使用的方法,第一是直接作为脚本执行,第二是import到其他的python脚本中被调用(模块重用)执行。因此if name == 'main': 的作用就是控制这两种情况执行代码的过程,在if name == 'main': 下的代码只有在第一种情况下(即文件作为脚本直接执行)才会被执行,而import到其他脚本中是不会被执行的。

引用W3Cschool上的一句解释:

name属性
一个模块被另一个程序第一次引入时,其主程序将运行。如果我们想在模块被引入时,模块中的某一程序块不执行,我们可以用name属性来使该程序块仅在该模块自身运行时执行。

说明:
每个模块都有一个name属性,当其值是'main'时,表明该模块自身在运行,否则是被引入。
namemain 底下是双下划线,是“_ _”去掉中间的空格。


2、Desired Capabilities:

Desired Capabilities信息.png

该截图来自官网Getting Started


3、官方提供的脚本示例,包含各种语言:sample-code

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容