一、前言                           

  前面讲了环境搭建和常用的元素定位,后续会持续以项目实践的方式去慢慢学习以及整理各方面的知识点,具体不会详细阐述,但会贴上完整代码,想要了解更多的可以直接网上查找资料哈,接下来用企业微信的应用(订单平台)做实践,环境配置:win10+Python 3.7.4+appium 1.16.0+unittest框架+真机andorid 8.0

注:关于移动端的,暂时就只做一个项目,因为移动端的扩展性没这么多,后续应该会以web端项目来实操

二、关于Appium实现中文输入  

  1、要实现中文输入的话,在初始化里加入下面的代码就行了

# 实现中文输入,使用unicodeKeyboard的编码方式来发送字符串
'unicodeKeyboard': True,
# 将键盘给隐藏起来
'resetKeyboard': True  

  2、如果Appium设置中文输入报错: Attempt to re-install io.appium.android.ime without first

    原因:漏卸载应用

    解决方法:查出所有的第三方安装包:一共3个,然后用adb卸载掉(adb shell pm list package -3或adb shell pm list package -3 | findstr appium),如图1

Appium+Python - 项目实践-LMLPHP

(图1:卸载应用)

  3、Appium 在 Android7.0 以上版本找不到元素的问题,解决方法:安装Uiautomator2(使用npm安装: npm install appium-uiautomator2-driver)

三、关于隐式等待和显示等待  

  1、WebDriverWait():显示等待,是针对于某个特定的元素设置的等待时间,在设置时间内,默认每隔一段时间检测一次当前页面某个元素是否存在,如果在规定的时间内找到了元素,则直接执行,即找到元素就执行相关操作,如果超过设置时间检测不到则抛出异常。默认检测频率为0.5s,默认抛出异常为:NoSuchElementException

  WebDriverWait()一般由unitl()或until_not()方法配合使用,until()调用该方法提供的驱动作为一个参数,直到返回值为True,unitl_not()调用该方法提供的驱动作为一个参数,直到返回值为False 。

  用法示例:

 1 from selenium.webdriver.support.wait import WebDriverWait
 2 from selenium.webdriver.support import expected_conditions as EC
 3 from selenium.webdriver.common.by import By
 4
 5 loc = ("xpath", "//android.view.View[contains(@text,'确定')]")
 6    try:
 7        # 显示等待用法一:EC.presence_of_element_located()直到元素出现
 8        e = WebDriverWait(self.driver, 3, 0.5).until(EC.presence_of_element_located(loc))
 9        # 显示等待用法二:用By
10        #WebDriverWait(self.driver, 3, 0.5).until(EC.presence_of_element_located(By.XPATH, "//android.view.View[contains(@text,'确定')]"))
11        # 显示等待用法三:用lambda函数
12        #e = WebDriverWait(self.driver, 3 ,0.5).until(lambda x: self.driver.find_element_by_xpath("//android.view.View[contains(@text,'确定')]"))
13        e.click()
14        print("成功选择非免付啦")
15     except:
16        pass

  2、implicitly_wait():隐式等待,是设置的全局等待。设置等待时间,是对页面中的所有元素设置加载时间,如果超出了设置时间的则抛出异常。隐式等待可以理解成在规定的时间范围内,浏览器在不停的刷新页面,直到找到相关元素或者时间结束。

  3、隐式等待和显示等待都存在时,超时时间取二者中较大的。

四、关于unittest框架     

  1、简要说明

  unittest是Python中自带的单元测试框架,它里面封装好了一些校验返回的结果方法和一些用例执行前的初始化操作。unittest单元测试框架不仅可以适用于单元测试,还可以适用web自动化测试用例的开发与执行,该测试框架可组织执行测试用例,并且提供了丰富的断言方法,判断测试用例是否通过,最终生成测试结果。

最基础的四个概念:TestCase,TestSuite,TestRunner,TestFixture

  2、运行流程

  先编写好TestCase,然后由TestLoader加载TestCase到TestSuite,其次由TextTestRunner来运行TestSuite,运行的结果保存在TextTestResult中,我们通过命令行或者unittest.main()执行时,main会调用TextTestRunner中的run来执行,或者我们可以直接通过TextTestRunner来执行用例

  3、unittest模块的各个属性说明

  1)unittest.TestCase:TestCase类,所有测试用例类继承的基本类。class BaiduTest(unittest.TestCase):

  2)unittest.main():使用它可以方便的将一个单元测试模块变为可直接运行的测试脚本,main()方法使用TestLoader类来搜索所有包含在该模块中以“test”命名开头的测试方法,并自动执行他们。执行方法的默认顺序是:根据ASCII码的顺序加载测试用例,数字与字母的顺序为:0-9,A-Z,a-z。所以以A开头的测试用例方法会优先执行,以a开头会后执行。

  3)unittest.TestSuite():unittest框架的TestSuite()类是用来创建测试套件的。

  4)unittest.TextTextRunner():unittest框架的TextTextRunner()类,通过该类下面的run()方法来运行suite所组装的测试用例,入参为suite测试套件。

  5)unittest.defaultTestLoader(): defaultTestLoader()类,通过该类下面的discover()方法可自动更具测试目录start_dir匹配查找测试用例文件(test*.py),并将查找到的测试用例组装到测试套件,因此可以直接通过run()方法执行discover。

  6)unittest.skip():装饰器,当运行用例时,有些用例可能不想执行等,可用装饰器暂时屏蔽该条测试用例。一种常见的用法就是比如说想调试某一个测试用例,想先屏蔽其他用例就可以用装饰器屏蔽。

    @unittest.skip(reason): skip(reason)装饰器:无条件跳过装饰的测试,并说明跳过测试的原因。

    @unittest.skipIf(reason):skipIf(condition,reason)装饰器:条件为真时,跳过装饰的测试,并说明跳过测试的原因。

    @unittest.skipUnless(reason):skipUnless(condition,reason)装饰器:条件为假时,跳过装饰的测试,并说明跳过测试的原因。

    @unittest.expectedFailure():expectedFailure()测试标记为失败。

  7)setUp():setUp()方法用于每个测试用例执行前的初始化工作。如测试用例中需要访问数据库,可以在setUp中建立数据库连接并进行初始化。如测试用例需要登录web,可以先实例化浏览器。

  8)tearDown():tearDown()方法用于每个测试用例执行之后的善后工作。如关闭数据库连接、关闭浏览器。

  9)setUpClass():setUpClass()方法用于所有测试用例前的设置工作。

  10)tearDownClass():tearDownClass()方法用于所有测试用例执行后的清理工作。

  11)addTest():addTest()方法是将测试用例添加到测试套件中。

  12)run():run()方法是运行测试套件的测试用例,入参为suite测试套件。

  13)assert*():在执行测试用例的过程中,最终用例是否执行通过,是通过判断测试得到的实际结果和预期结果是否相等决定的。

  4、添加用例运行方式

  1)用法一:用addTests方法单个添加用例

import unittest

if __name__ == '__main__':
    suite = unittest.TestSuite()
    # 单个添加测试用例
    suite.addTest(TestMathFunc("test_multi"))
    suite.addTest(TestMathFunc("test_divide"))
    runner = unittest.TextTestRunner(verbosity=2)
    runner.run(suite)

  2)用法二:将用例添加到一个用例集中,再通过suite统一运行

import unittest

if __name__ == '__main__':
    suite = unittest.TestSuite()
    # 将测试用例添加到一个用例集中
    tests = [TestMathFunc("test_add"), TestMathFunc("test_minus")]
    suite.addTests(tests)  # 将测试用例列表添加到测试组中
    runner = unittest.TextTestRunner(verbosity=2)
    runner.run(suite)

  3)用法三:这是最实用的方法,引用discover()方法,匹配指定文件夹下以test开头的测试用例

import unittest

if __name__ == '__main__':
    path = './testcase'
    all_cases = unittest.defaultTestLoader.discover(path, 'test*.py')
    # 找到某个目录下所有的以test开头的Python文件里面的测试用例
    runner = unittest.TextTestRunner(verbosity=2)
    runner.run(all_cases)

五、BeautifulReport测试报告  

  1、如何使用?

  1)pip安装:pip install BeautifulReport

  2)使用示例:

import unittest
from BeautifulReport import BeautifulReport

if __name__ == '__main__':
    #"."表示当前目录,"*tests.py"匹配当前目录下所有tests.py结尾的用例
    test_suite = unittest.defaultTestLoader.discover('./tests', pattern='test*.py')
   result = BeautifulReport(test_suite)
   result.report(filename='测试报告', description='测试deafult报告', report_dir='report', theme='theme_default')

  2、简要说明:

  1)BeautifulReport.report

    report (
        filename -> 测试报告名称, 如果不指定默认文件名为report.html
        description -> 测试报告用例名称展示
        report_dir='.' -> 报告文件写入路径
        theme='theme_default' -> 报告主题样式 theme_default theme_cyan theme_candy theme_memories
        )

  2)BeautifulReport.add_test_img:如果使用报告过程中需要把测试报告的截图放在报告中, 可以使用add_test_img方法

  3)add_test_img ( *pargs ):可以在测试用例上挂载一个装饰器, 实例内容如下:

    默认存放的图片路径是img, 需要在当前测试项目的启动路径下, 创建一个img文件夹

    传递给装饰器的图片,在运行测试前可以不存在, 运行测试之后生成即可.

    当文件在报告中展示后, 想要看到原图, 可以点击报告中的缩略图查看完整的截图

六、完整项目代码示例    

  1、项目目录结构,如图2

Appium+Python - 项目实践-LMLPHP

(图2:项目目录结构示例)

  2、测试用例text_wxwork.py

注:可能会涉及到敏感信息,所以有些case的注释和text会用XXX代替  

 1 from appium import webdriver  
 2 import unittest 
 3 import time
 4
import datetime 5 from selenium.webdriver.support.ui import WebDriverWait 6 from selenium.webdriver.support import expected_conditions as EC 7 from BeautifulReport import BeautifulReport 8 import os 9 from selenium.webdriver.common.by import By 10 11 class wxworkTest(unittest.TestCase): 12 '''企业微信-XXX移动端''' 13 @classmethod 14 def setUpClass(cls): 15 ''' 所有的测试方法运行前运行,使用@classmethod装饰器进行修饰,整个测试过程中只执行一次 ''' 16 wxwork = { 17 'platformName': 'Android', # android的apk 18 'deviceName': 'WTKDU16905017501', # 手机设备名称,通过adb devices查看 19 'platformVersion': '8.0', # android系统的版本号 20 'appPackage': 'com.tencent.wework', # apk包名 21 'appActivity': '.launch.LaunchSplashActivity', # apk的启动Activity 22 #'unicodeKeyboard': True, # 实现中文输入,使用unicodeKeyboard的编码方式来发送字符串 23 #'resetKeyboard': True # 将键盘给隐藏起来 24 } 25 cls.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", wxwork) # 连接appium 26 cls.driver.implicitly_wait(10) # 隐式等待,最长等待10s 27 # 切换到工作台 28 cls.driver.find_element_by_xpath("//*[@text='工作台']").click() 29 # 调用上滑方法,找到应用并进入 30 cls.up(4, 500) 31 time.sleep(1) 32 cls.driver.find_element_by_xpath("//*[@text='XXX测试']").click() 33 # 在移动端应用进入主页 34 cls.driver.find_element_by_xpath("//*[@text='XXX采购']").click() 35 print("已进入XXX移动端/我的工作台") 36 37 @classmethod 38 def tearDownClass(cls): 39 ''' 所有的测试方法运行后运行,使用@classmethod装饰器进行修饰,整个测试过程中只执行一次 ''' 40 cls.driver.quit() # 退出应用 41 print("已退出企业微信") 42 43 @classmethod 44 def getsize(self): 45 '''定义方法:自动获取手机屏幕''' 46 x = self.driver.get_window_size()['width'] 47 y = self.driver.get_window_size()['height'] 48 return (x, y) 49 50 @classmethod 51 def up(self,n,t): 52 '''定义方法:上滑手势操作''' 53 screensize = self.getsize() 54 x1 = int(screensize[0] * 0.5) 55 y1 = int(screensize[1] * 0.7) 56 y2 = int(screensize[1] * 0.3) 57 for i in range(0, n): 58 self.driver.swipe(x1, y1, x1, y2, t) 59 print("", i + 1, "次上滑成功啦") 60 61 def save_img1(self): 62 """直接指定路径,并用当前时间生成截图""" 63 # 获取当前时间,用于命名截图 64 pic_time = datetime.datetime.now().strftime("%Y-%m-%d %H-%M-%S") 65 # 截图保存写入的路径中 66 self.driver.get_screenshot_as_file("E:\\08PyCharmProject\\Case1\\img\\" + pic_time + ".png") 67 68 def save_img(self, img_name): 69 '''用于放在测试报告的截图,os.path.abspath:返回当前目录的绝对路径,传入一个img_name,并存储到指定路径下''' 70 self.driver.get_screenshot_as_file('{}\{}.png'.format(os.path.abspath('E:\\08PyCharmProject\\Case1\\img'), img_name)) 71 72 def editClear(self, text): 73 '''定义清除文本框方法''' 74 # 123代表光标移动到末尾 75 self.driver.keyevent(123) 76 for i in range(0, len(text)): 77 self.driver.keyevent(67) # 67退格键 78 # 清空后回车 79 self.driver.keyevent(66) 80 81 def main_purchurse(self): 82 '''定义重新进入主页面的方法''' 83 '''用text不管用,用xpath的话,每个用例最终停留的界面不同,所以也不行,最后忍痛决定用tap''' 84 # 坐标点击我的工作台 85 self.driver.tap([(720, 1644), (1080, 1792)], 500) 86 # 坐标点击XXX 87 self.driver.tap([(0, 1644),(360, 1792)],500) 88 print("已在XXX主页面") 89 90 def detail_purchurse(self): 91 '''定义重新进入XXX明细页面的方法''' 92 # 坐标点击我的工作台 93 self.driver.tap([(720, 1644), (1080, 1792)], 500) 94 # 坐标点击XXX 95 self.driver.tap([(0, 1644), (360, 1792)], 500) 96 # 通过定位电话图标,点击进入XXX明细页面 97 self.driver.find_element_by_xpath("//*[@text='ABs3ach']").click() 98 print("已在XXX明细页面") 99 100 def test_case1(self): 101 '''case1:我的工作台:各种类型的订单统计跳转功能测试''' 103 self.driver.find_element_by_xpath("//*[@text='未发货']").click() 104 print("case1执行成功:完成了工作台的所有跳转测试") 124 125 @BeautifulReport.add_test_img('test_case2') # 在测试用例中挂载一个装饰器,报错自动截图 126 def test_case2(self): 127 '''case2:我的工作台/XXX/XXX,来回切换(报错自动截图贴报告,不报错时用时间命名的截图,不贴报告)''' 128 self.main_purchurse() # 调用方法:我的工作台切换到XXX 129 # XXX切换到XXXX:tap定位 130 self.driver.tap([(360, 1644), (720, 1792)], 500) 131 print("成功切换到XXXX模块") 132 self.save_img1() # 截图断言弹窗提示语,没报错则调用另一个方法截图,没有入参 另一个方法不会附在测试报告上 133 print("case2执行成功:完成底部大模块的切换测试")150 151 @BeautifulReport.add_test_img('test_case4') #报错自动截图 152 def test_case4(self): 153 '''case4:XXX主表:待下单状态的功能校验(报错自动截图贴报告,不报错也截图,不贴报告)''' 154 self.main_purchurse() # 调用方法:我的工作台切换到XXX 155 # 搜索功能测试:回车键搜索 156 self.driver.find_element_by_xpath("//android.widget.EditText[@text='供应商/采购员']").send_keys("M0001") 157 self.driver.keyevent(66) # 回车键搜索 158 print("搜索成功") 159 # 获取文本框的文本,用于传参给editClear函数 160 get_text = self.driver.find_element_by_xpath("//android.widget.EditText[@text='供应商/采购员']").get_attribute('text') 161 # 再次点击文本框,用于清空操作 162 self.driver.find_element_by_xpath("//android.widget.EditText[@text='供应商/采购员']").click() 163 self.editClear(get_text) # 调用方法:清空文本框的值 164 # 获取清空后文本框的文本,用于判断是否删除成功 165 get_text1 = self.driver.find_element_by_xpath("//android.widget.EditText[@text='供应商/采购员']").get_attribute("text") 166 print((get_text1)) 167 if get_text1 == "供应商/采购员": 168 print("文本框删除成功") 169 else: 170 print("文本框删除失败") 171 # 点击分享 172 self.driver.find_element_by_xpath("//*[@text='分享']").click() 173 print("您想要分享给微信好友,还是企业微信好友呢?") 174 self.save_img('test_case4') # 不报错也自动截图 175 self.driver.find_element_by_xpath("//*[@text='取消']").click() 176 print("取消分享") 177 print("case4执行成功:完成XXX-待下单状态的功能测试") 178 179 def test_case5(self): 180 '''case5:XXX明细页的状态切换测试''' 181 self.detail_purchurse() # 调用方法:进入XXX明细页 182 # 点击采购中tab列表,因为text有时不起作用,保险起见这里用了兄弟元素定位 183 self.driver.find_element_by_xpath("//android.widget.Button[@text='一键操作']/../android.view.View[4]//*[contains(@text,'采购中')]").click() 184 print("已进入XXX明细页的采购中列表") 185 '''这里删除了一些不重要的'''193
       print("case5执行成功:完成版料明细页的状态切换") 194 195 def test_case6(self): 196 '''case6:XXX明细页:待下单功能校验1-搜索区''' 197 self.detail_purchurse() # 调用方法:进入XXX明细页 198 # 搜索框输入sku,回车搜索 199 self.driver.find_element_by_xpath("//*[@class='android.widget.EditText']").send_keys("F00000583") 200 self.driver.keyevent(66) 201 print("sku搜索成功") 202 # 获取文本框的文本,用于传参给editClear函数 203 get_text = self.driver.find_element_by_xpath("//*[@class='android.widget.EditText']").get_attribute('text') 204 print(get_text) 205 # 再次点击文本框,用于清空操作 206 self.driver.find_element_by_xpath("//*[@class='android.widget.EditText']").click() 207 self.editClear(get_text) # 调用方法:清空文本框的值 208 # 获取清空后文本框的文本,用于判断是否删除成功 209 get_text1 = self.driver.find_element_by_xpath("//*[@class='android.widget.EditText']").get_attribute("text") 211 print((get_text1)) 212 if get_text1 == "供应商/采购单号/物料名称/sku/设计款号/采购员": 213 print("文本框删除成功") 214 else: 215 print("文本框删除失败") 216 # 目标交期排序功能测试 217 self.driver.find_element_by_xpath("//*[@text='目标交期']").click() 218 print("目标交期筛选成功") 219 # 筛选功能测试(兄弟元素定位) 220 self.driver.find_element_by_xpath("//android.widget.Button[@text='一键操作']/../android.widget.Button").click() 221 self.driver.find_element_by_xpath("//android.widget.Button[@text='超期']").click()
225 self.driver.find_element_by_xpath("//android.widget.Button[@text='重置']").click() 226 self.driver.find_element_by_xpath("//android.widget.Button[@text='超期']").click() 227 self.driver.find_element_by_xpath("//android.widget.Button[@text='确定']").click() 228 print("成功输入超期筛选条件") 229 print("搜索成功") 230 # 一键操作功能测试:确认采购 231 self.driver.find_element_by_xpath("//android.widget.Button[@text='一键操作']").click() 232 # 定位复选框,点击确认采购:兄弟元素定位,因为有多个android.view.View兄弟,所以要带上[] 233 self.driver.find_element_by_xpath("//android.view.View[contains(@text,'地址')]/../android.view.View[1]/android.view.View[1]").click() 234 print("勾选上啦") 235 # 确认采购,虽然有text,但没定位成功,所以这里用的是ia+class组合定位 236 self.driver.find_element_by_xpath("//*[@resource-id='container']/android.widget.Button[3]").click() 237 print("确认采购成功") 238 self.driver.find_element_by_xpath("//*[@text='完成']").click() 239 print("case6执行成功:完成搜索与一键确认采购测试") 240 241 def test_case7(self): 242 '''case7:XXX明细页:待下单的功能校验2-卡片区(不报错时也截图)''' 243 self.detail_purchurse() # 调用方法:进入XXX明细页 244 # 点击放大图片,方法一:从最父级,一层一层往下找 245 self.driver.find_element_by_xpath("//*[@resource-id='container']/android.view.View[5]/android.view.View[1]/android.view.View[1]/android.view.View[1]/android.view.View[4]/android.widget.Image").click() 246 print("放大图片") 247 self.save_img('test_case7') # 调用截图方法 248 time.sleep(2) 249 self.driver.keyevent(4) # 返回键 250 # 点击放大图片,方法二:根据兄弟元素(text:地址)定位到自己,再往下找 251 # 根据兄弟元素定位到自己,再往下找 252 self.driver.find_element_by_xpath("//android.view.View[contains(@text,'地址')]/../android.view.View/android.widget.Image").click() 253 self.driver.keyevent(4) 254 print("再次放大图片") 255 print("case7执行成功:完成卡片区的图片放大功能测试") 256 257 def test_case8(self): 258 '''case8:XXX明细页:采购中状态-通知发货功能测试''' 259 self.detail_purchurse() # 调用方法:进入XXX明细页 260 # 点击采购中tab列表,因为text有时不起作用,保险起见这里用了兄弟元素定位 261 self.driver.find_element_by_xpath("//android.widget.Button[@text='一键操作']/../android.view.View[4]//*[contains(@text,'采购中')]").click() 262 print("已进入XXX明细页的采购中列表") 263 # 点击通知发货 264 self.driver.find_element_by_xpath("//*[@text='通知发货']").click() 265 print("已进入发货窗口") 266 # 打印本次采购单价输入框的文本 267 get_text = self.driver.find_element_by_xpath("//*[contains(@text,'*本次采购单价')]/../android.view.View/android.widget.EditText").get_attribute('text') 268 print(get_text) 269 # 定位到本次采购单价,并清除文本框的值,重新输入新的采购单价 270 self.driver.find_element_by_xpath("//*[contains(@text,'*本次采购单价')]/../android.view.View/android.widget.EditText").click() 271 self.editClear(get_text) 272 self.driver.find_element_by_xpath("//*[contains(@text,'*本次采购单价')]/../android.view.View/android.widget.EditText").send_keys(1) 273 # 打印是否免付选择框的文本 274 text = self.driver.find_element_by_xpath("//*[contains(@text,'*是否免付')]/../android.view.View/android.view.View").text 275 print(text) 276 # 点击是否免付选择框 277 self.driver.find_element_by_xpath("//*[contains(@text,'*是否免付')]/../android.view.View/android.view.View").click() 278 print("成功弹窗") 279 # 方法一:弹窗默认为是免付,直接确定 280 self.driver.find_element_by_xpath("//android.view.View[contains(@text,'确定')]").click() 281 # 方法二:再次点击出现免付弹窗,向上滑动,选到否免付后,确定 282 self.driver.find_element_by_xpath("//*[contains(@text,'*是否免付')]/../android.view.View/android.view.View").click() 283 self.up(2, 500) 284 loc = ("xpath", "//android.view.View[contains(@text,'确定')]") 285 try: 286 # 显示等待,EC.presence_of_element_located()直到元素出现 287 e = WebDriverWait(self.driver, 3, 0.5).until(EC.presence_of_element_located(loc)) 288 # 也可以用By 289 #WebDriverWait(self.driver, 3, 0.5).until(EC.presence_of_element_located(By.XPATH, "//android.view.View[contains(@text,'确定')]")) 290 # 也可以用lambda函数 291 #e = WebDriverWait(self.driver, 3 ,0.5).until(lambda x: self.driver.find_element_by_xpath("//android.view.View[contains(@text,'确定')]")) 292 e.click() 293 print("成功选择非免付啦") 294 except: 295 pass 296 # 上传图片 297 self.driver.find_element_by_xpath("//android.view.View[contains(@text,'添加图片')]").click() 298 self.driver.find_element_by_xpath("//*[@resource-id='com.tencent.wework:id/aqa'][contains(@text,'其他方式')]").click() 299 self.driver.find_element_by_xpath("//*[@resource-id='com.android.documentsui:id/dir_list']/android.widget.LinearLayout[1]").click() 300 print("成功上传图片") 301 # 确定发货 302 self.driver.find_element_by_xpath("//android.widget.Button[contains(@text,'确定')]").click() 303 print("发货成功") 304 305 @unittest.skip("暂时不执行该用例") 306 def test_case9(self): 307 '''case9:订单明细页:测试''' 308 309 @BeautifulReport.add_test_img('test_case90') 310 def test_case90(self): 311 '''用于看测试报告的报错截图''' 312 self.main_purchurse() # 调用方法:进入XXX主表 313 self.driver.find_element_by_xpath("111") 314 print("case90:故意输入错误的定位,用于报错截图")

  3、执行用例excute_case.py

 1 import unittest
 2 from BeautifulReport import BeautifulReport
 3 import datetime
 4
 5 if __name__ == '__main__':
 6     # 加载测试用例:指定E:\\08PyCharmProject\\Case1\\test_case目录,“test*.py”匹配指定目录下所有test开头的.py文件
 7     test_suite = unittest.defaultTestLoader.discover('E:\\08PyCharmProject\\Case1\\test_case', pattern='test*.py')
 8     # 获取当前时间,用于命名测试报告标题
 9     now = datetime.datetime.now().strftime('%Y-%m-%d %H-%M-%S')
10     # 将用例加到对象中
11     result = BeautifulReport(test_suite)
12     # report方法实现了用例的执行、用例执行结束的结果统计、生成测试报告等操作
13     # :filename -> 测试报告名称, 如果不指定默认文件名为report.html,description -> 测试报告用例名称展示,report_dir-> 报告文件写入路径
14     result.report(filename='测试报告'+str(now), description='自动化测试', report_dir='E:\\08PyCharmProject\\Case1\\report')

  4、执行结束后的img和report展示

  1)img展示,如图3

Appium+Python - 项目实践-LMLPHP

(图3:img目录展示截图)

  2)report展示,如图4-6

Appium+Python - 项目实践-LMLPHP

(图4:report目录展示测试报告)

Appium+Python - 项目实践-LMLPHP

 (图5:html测试报告1)

Appium+Python - 项目实践-LMLPHP

(图6:html测试报告2-详细数据,报错的用例会有截图)

05-10 21:44