用 Pytest+Appium+Allure 做 UI 自动化测试的那些事儿

本文首发于 TesterHome 社区, 文末有福利 !链接 https://testerhome.com/topics/19327

前言

做 UI 自动化测试有段时间了,在 TesterHome
社区看了大量文章,也在网上搜集了不少资料,加上自己写代码、调试过程中摸索了很多东西,踩了不少坑,才有了这篇文章。希望能给做 UI
自动化测试小伙伴们提供些许帮助。

文本主要介绍用 Pytest+Allure+Appium 实现 UI
自动化测试过程中的一些好用的方法和避坑经验。文章可能有点干,看官们多喝水!O(∩_∩)O~

主要用了啥:

  • Python3
  • Appium
  • Allure-pytest
  • Pytest

Appium 不常见却好用的方法

1. Appium 直接执行 adb shell 方法

          1.  # Appium 启动时增加 --relaxed-security 参数 Appium 即可执行类似adb shell的方法
          2.     
          3.       2. > appium -p 4723 --relaxed-security
          4.     
          5.     
          6.     
          7.     
          8.           1. # 使用方法
          9.     
          10.      2. def adb_shell(self, command, args, includeStderr=False):
          11.     
          12.       3.     """
          13.     
          14.       4.     appium --relaxed-security 方式启动
          15.     
          16.       5.     adb_shell('ps',['|','grep','android'])
          17.     
          18.       6.   
          19.     
          20.     
          21.       7.     :param command:命令
          22.     
          23.       8.     :param args:参数
          24.     
          25.       9.     :param includeStderr: 为 True 则抛异常
          26.     
          27.       10.     :return:
          28.     
          29.       11.     """
          30.     
          31.       12.     result = self.driver.execute_script('mobile: shell', {
          32.     
          33.       13.         'command': command,
          34.     
          35.       14.         'args': args,
          36.     
          37.       15.         'includeStderr': includeStderr,
          38.     
          39.       16.         'timeout': 5000
          40.     
          41.       17.         })
          42.     
          43.       18.     return result['stdout']
          44.     
          45.     
          46.     

2. Appium 直接截取元素图片的方法

          1.  element = self.driver.find_element_by_id('cn.xxxxxx:id/login_sign')
          2.     
          3.       2. pngbyte = element.screenshot_as_png
          4.     
          5.       3. image_data = BytesIO(pngbyte)
          6.     
          7.       4. img = Image.open(image_data)
          8.     
          9.       5. img.save('element.png')
          10.    
          11.       6. # 该方式能直接获取到登录按钮区域的截图
          12.     
          13.     
          14.     

3. Appium 直接获取手机端日志

          1.  # 使用该方法后,手机端 logcat 缓存会清除归零,从新记录
          2.     
          3.       2. # 建议每条用例执行完执行一边清理,遇到错误再保存减少陈余 log 输出
          4.     
          5.       3. # Android
          6.     
          7.       4. logcat = self.driver.get_log('logcat')
          8.     
          9.       5.   
          10.    
          11.     
          12.       6. # iOS 需要安装 brew install libimobiledevice
          13.     
          14.       7. logcat = self.driver.get_log('syslog')
          15.     
          16.       8.   
          17.     
          18.     
          19.       9. # web 获取控制台日志
          20.     
          21.       10. logcat = self.driver.get_log('browser')
          22.     
          23.       11.   
          24.     
          25.     
          26.       12. c = '\n'.join([i['message'] for i in logcat])
          27.     
          28.       13. allure.attach(c, 'APPlog', allure.attachment_type.TEXT)
          29.     
          30.       14. #写入到 allure 测试报告中
          31.     
          32.     
          33.     
4. Appium 直接与设备传输文件
          1.  # 发送文件
          2.     
          3.       2. #Android
          4.     
          5.       3. driver.push_file('/sdcard/element.png', source_path='D:\works\element.png')
          6.     
          7.       4.   
          8.     
          9.     
          10.      5. # 获取手机文件
          11.     
          12.       6. png = driver.pull_file('/sdcard/element.png')
          13.     
          14.       7. with open('element.png', 'wb') as png1:
          15.     
          16.       8.     png1.write(base64.b64decode(png))
          17.     
          18.       9.   
          19.     
          20.     
          21.       10. # 获取手机文件夹,导出的是zip文件
          22.     
          23.       11. folder = driver.pull_folder('/sdcard/test')
          24.     
          25.       12. with open('test.zip', 'wb') as folder1:
          26.     
          27.       13.     folder1.write(base64.b64decode(folder))
          28.     
          29.       14.   
          30.     
          31.     
          32.       15. # iOS
          33.     
          34.       16. # 需要安装 ifuse
          35.     
          36.       17. # > brew install ifuse 或者 > brew cask install osxfuse 或者 自行搜索安装方式
          37.     
          38.       18.   
          39.     
          40.     
          41.       19. driver.push_file('/Documents/xx/element.png', source_path='D:\works\element.png')
          42.     
          43.       20.   
          44.     
          45.     
          46.       21. # 向 App 沙盒中发送文件
          47.     
          48.       22. # iOS 8.3 之后需要应用开启 UIFileSharingEnabled 权限不然会报错
          49.     
          50.       23. bundleId = 'cn.xxx.xxx' # APP名字
          51.     
          52.       24. driver.push_file('@{bundleId}/Documents/xx/element.png'.format(bundleId=bundleId), source_path='D:\works\element.png')
          53.     
          54.     
          55.     

Pytest 与 Unittest 初始化上的区别

很多人都使用过 Unitest,先说一下 Pytest 和 Unitest 在 Hook method上的一些区别:

1. Pytest 与 Unitest 类似,但有些许区别

以下是 Pytest

          1. class TestExample:
          2.     
          3.       2.     def setup(self):
          4.     
          5.       3.         print("setup             class:TestStuff")
          6.     
          7.       4.   
          8.     
          9.     
          10.      5.     def teardown(self):
          11.     
          12.       6.         print ("teardown          class:TestStuff")
          13.     
          14.       7.   
          15.     
          16.     
          17.       8.     def setup_class(cls):
          18.     
          19.       9.         print ("setup_class       class:%s" % cls.__name__)
          20.     
          21.       10.   
          22.     
          23.     
          24.       11.     def teardown_class(cls):
          25.     
          26.       12.         print ("teardown_class    class:%s" % cls.__name__)
          27.     
          28.       13.   
          29.     
          30.     
          31.       14.     def setup_method(self, method):
          32.     
          33.       15.         print ("setup_method      method:%s" % method.__name__)
          34.     
          35.       16.   
          36.     
          37.     
          38.       17.     def teardown_method(self, method):
          39.     
          40.       18.         print ("teardown_method   method:%s" % method.__name__)
          41.     
          42.     
          43.     

2. 使用 pytest.fixture()

          1.  @pytest.fixture()
          2.     
          3.       2. def driver_setup(request):
          4.     
          5.       3.     request.instance.Action = DriverClient().init_driver('android')
          6.     
          7.       4.     def driver_teardown():
          8.     
          9.       5.         request.instance.Action.quit()
          10.    
          11.       6.     request.addfinalizer(driver_teardown)
          12.     
          13.     
          14.     

初始化实例

1. setup_class 方式调用

          1.  class Singleton(object):
          2.     
          3.       2.     """单例
          4.     
          5.       3.     ElementActions 为自己封装操作类"""
          6.     
          7.       4.     Action = None
          8.     
          9.       5.   
          10.    
          11.     
          12.       6.     def __new__(cls, *args, **kw):
          13.     
          14.       7.         if not hasattr(cls, '_instance'):
          15.     
          16.       8.             desired_caps={}
          17.     
          18.       9.             host = "http://localhost:4723/wd/hub"
          19.     
          20.       10.             driver = webdriver.Remote(host, desired_caps)
          21.     
          22.       11.             Action = ElementActions(driver, desired_caps)
          23.     
          24.       12.             orig = super(Singleton, cls)
          25.     
          26.       13.             cls._instance = orig.__new__(cls, *args, **kw)
          27.     
          28.       14.             cls._instance.Action = Action
          29.     
          30.       15.         return cls._instance
          31.     
          32.       16.   
          33.     
          34.     
          35.       17. class DriverClient(Singleton):
          36.     
          37.       18.     pass
          38.     
          39.     
          40.     

测试用例中调用

          1. class TestExample:
          2.     
          3.       2.     def setup_class(cls):
          4.     
          5.       3.         cls.Action = DriverClient().Action
          6.     
          7.       4.   
          8.     
          9.     
          10.      5.     def teardown_class(cls):
          11.     
          12.       6.         cls.Action.clear()
          13.     
          14.       7.   
          15.     
          16.     
          17.       8.   
          18.     
          19.     
          20.       9.     def test_demo(self)
          21.     
          22.       10.         self.Action.driver.launch_app()
          23.     
          24.       11.         self.Action.set_text('123')
          25.     
          26.     
          27.     

2. pytest.fixture() 方式调用

          1.  class DriverClient():
          2.     
          3.       2.   
          4.     
          5.     
          6.       3.     def init_driver(self,device_name):
          7.     
          8.       4.         desired_caps={}
          9.     
          10.      5.         host = "http://localhost:4723/wd/hub"
          11.     
          12.       6.         driver = webdriver.Remote(host, desired_caps)
          13.     
          14.       7.         Action = ElementActions(driver, desired_caps)
          15.     
          16.       8.         return Action
          17.     
          18.       9.   
          19.     
          20.     
          21.       10.   
          22.     
          23.     
          24.       11.   
          25.     
          26.     
          27.       12. # 该函数需要放置在 conftest.py, pytest 运行时会自动拾取
          28.     
          29.       13. @pytest.fixture()
          30.     
          31.       14. def driver_setup(request):
          32.     
          33.       15.     request.instance.Action = DriverClient().init_driver()
          34.     
          35.       16.     def driver_teardown():
          36.     
          37.       17.         request.instance.Action.clear()
          38.     
          39.       18.     request.addfinalizer(driver_teardown)
          40.     
          41.     
          42.     

测试用例中调用

          1. #该装饰器会直接引入driver_setup函数
          2.     
          3.       2. @pytest.mark.usefixtures('driver_setup')
          4.     
          5.       3. class TestExample:
          6.     
          7.       4.   
          8.     
          9.     
          10.      5.     def test_demo(self):
          11.     
          12.       6.         self.Action.driver.launch_app()
          13.     
          14.       7.         self.Action.set_text('123')
          15.     
          16.     
          17.     

Pytest 参数化方法

1. 第一种方法 parametrize 装饰器参数化方法

          1.  @pytest.mark.parametrize(('kewords'), [(u"小明"), (u"小红"), (u"小白")])
          2.     
          3.       2. def test_kewords(self,kewords):
          4.     
          5.       3.     print(kewords)
          6.     
          7.       4.   
          8.     
          9.     
          10.      5. # 多个参数
          11.     
          12.       6. @pytest.mark.parametrize("test_input,expected", [
          13.     
          14.       7.     ("3+5", 8),
          15.     
          16.       8.     ("2+4", 6),
          17.     
          18.       9.     ("6*9", 42),
          19.     
          20.       10. ])
          21.     
          22.       11. def test_eval(test_input, expected):
          23.     
          24.       12.     assert eval(test_input) == expected
          25.     
          26.     
          27.     

2.第二种方法,使用 pytest hook 批量加参数化

          1.  #  conftest.py
          2.     
          3.       2. def pytest_generate_tests(metafunc):
          4.     
          5.       3.     """
          6.     
          7.       4.     使用 hook 给用例加加上参数
          8.     
          9.       5.     metafunc.cls.params 对应类中的 params 参数
          10.    
          11.       6.   
          12.     
          13.     
          14.       7.     """
          15.     
          16.       8.     try:
          17.     
          18.       9.         if metafunc.cls.params and metafunc.function.__name__ in metafunc.cls.params: ## 对应 TestClass params
          19.     
          20.       10.           funcarglist = metafunc.cls.params[metafunc.function.__name__]
          21.     
          22.       11.           argnames = list(funcarglist[0])
          23.     
          24.       12.           metafunc.parametrize(argnames, [[funcargs[name] for name in argnames] for funcargs in funcarglist])
          25.     
          26.       13.     except AttributeError:
          27.     
          28.       14.         pass
          29.     
          30.       15.   
          31.     
          32.     
          33.       16. # test_demo.py
          34.     
          35.       17. class TestClass:
          36.     
          37.       18.     """
          38.     
          39.       19.     :params 对应 hook 中 metafunc.cls.params
          40.     
          41.       20.     """
          42.     
          43.       21.     # params = Parameterize('TestClass.yaml').getdata()
          44.     
          45.       22.   
          46.     
          47.     
          48.       23.     params = {
          49.     
          50.       24.         'test_a': [{'a': 1, 'b': 2}, {'a': 1, 'b': 2}],
          51.     
          52.       25.         'test_b': [{'a': 1, 'b': 2}, {'a': 1, 'b': 2}],
          53.     
          54.       26.     }
          55.     
          56.       27.     def test_a(self, a, b):
          57.     
          58.       28.         assert a == b
          59.     
          60.       29.     def test_b(self, a, b):
          61.     
          62.       30.         assert a == b
          63.     
          64.     
          65.     

Pytest 用例依赖关系

使用 pytest-dependency 库可以创造依赖关系。

当上层用例没通过,后续依赖关系用例将直接跳过,可以跨 Class 类筛选。如果需要跨 .py 文件运行 需要将 site- packages/pytest_dependency.py 文件的

          1. class DependencyManager(object):
          2.     
          3.       2.     """Dependency manager, stores the results of tests.
          4.     
          5.       3.     """
          6.     
          7.       4.   
          8.     
          9.     
          10.      5.     ScopeCls = {'module':pytest.Module, 'session':pytest.Session}
          11.     
          12.       6.   
          13.     
          14.     
          15.       7.     @classmethod
          16.     
          17.       8.     def getManager(cls, item, scope='session'): # 这里修改成 session
          18.     
          19.     
          20.     

如果

          1. > pip install pytest-dependency
          2.     
          3.     
          4.     
          5.     
          6.           1. class TestExample(object):
          7.     
          8.       2.   
          9.     
          10.    
          11.       3.     @pytest.mark.dependency()
          12.     
          13.       4.     def test_a(self):
          14.     
          15.       5.         assert False
          16.     
          17.       6.   
          18.     
          19.     
          20.       7.     @pytest.mark.dependency()
          21.     
          22.       8.     def test_b(self):
          23.     
          24.       9.         assert False
          25.     
          26.       10.   
          27.     
          28.     
          29.       11.     @pytest.mark.dependency(depends=["TestExample::test_a"])
          30.     
          31.       12.     def test_c(self):
          32.     
          33.       13.         # TestExample::test_a 没通过则不执行该条用例
          34.     
          35.       14.         # 可以跨 Class 筛选
          36.     
          37.       15.         print("Hello I am in test_c")
          38.     
          39.       16.   
          40.     
          41.     
          42.       17.     @pytest.mark.dependency(depends=["TestExample::test_a","TestExample::test_b"])
          43.     
          44.       18.     def test_d(self):
          45.     
          46.       19.         print("Hello I am in test_d")
          47.     
          48.     
          49.     
          50.     
          51.           1. pytest -v test_demo.py
          52.     
          53.       2. 2 failed
          54.     
          55.       3.          - test_1.py:6 TestExample.test_a
          56.     
          57.       4.          - test_1.py:10 TestExample.test_b
          58.     
          59.       5. 2 skipped
          60.     
          61.     
          62.     

Pytest 自定义标记,执行用例筛选作用

1. 使用 @pytest.mark 模块给类或者函数加上标记,用于执行用例时进行筛选

          1.  @pytest.mark.webtest
          2.     
          3.       2. def test_webtest():
          4.     
          5.       3.     pass 
          6.     
          7.       4.   
          8.     
          9.     
          10.      5.   
          11.     
          12.     
          13.       6. @pytest.mark.apitest
          14.     
          15.       7. class TestExample(object):
          16.     
          17.       8.     def test_a(self):
          18.     
          19.       9.         pass
          20.     
          21.       10.   
          22.     
          23.     
          24.       11.     @pytest.mark.httptest
          25.     
          26.       12.     def test_b(self):
          27.     
          28.       13.         pass
          29.     
          30.     
          31.     

仅执行标记 webtest 的用例

          1. pytest -v -m webtest
          2.     
          3.       2.   
          4.     
          5.     
          6.       3. Results (0.03s):
          7.     
          8.       4.        1 passed
          9.     
          10.      5.        2 deselected
          11.     
          12.     
          13.     

执行标记多条用例

          1. pytest -v -m "webtest or apitest"
          2.     
          3.       2.   
          4.     
          5.     
          6.       3. Results (0.05s):
          7.     
          8.       4.        3 passed
          9.     
          10.    
          11.     

仅不执行标记 webtest 的用例

          1. pytest -v -m "not webtest"
          2.     
          3.       2.   
          4.     
          5.     
          6.       3. Results (0.04s):
          7.     
          8.       4.        2 passed
          9.     
          10.      5.        1 deselected
          11.     
          12.     
          13.     

不执行标记多条用例

          1. pytest -v -m "not webtest and not apitest"
          2.     
          3.       2.   
          4.     
          5.     
          6.       3. Results (0.02s):
          7.     
          8.       4.        3 deselected
          9.     
          10.    
          11.     

2. 根据 test 节点选择用例

          1.  pytest -v Test_example.py::TestClass::test_a
          2.     
          3.       2. pytest -v Test_example.py::TestClass
          4.     
          5.       3. pytest -v Test_example.py Test_example2.py
          6.     
          7.     
          8.     

3. 使用 pytest hook 批量标记用例

          1.  # conftet.py
          2.     
          3.       2.   
          4.     
          5.     
          6.       3. def pytest_collection_modifyitems(items):
          7.     
          8.       4.     """
          9.     
          10.      5.     获取每个函数名字,当用例中含有该字符则打上标记
          11.     
          12.       6.     """
          13.     
          14.       7.     for item in items:
          15.     
          16.       8.         if "http" in item.nodeid:
          17.     
          18.       9.             item.add_marker(pytest.mark.http)
          19.     
          20.       10.         elif "api" in item.nodeid:
          21.     
          22.       11.             item.add_marker(pytest.mark.api)
          23.     
          24.     
          25.     
          26.     
          27.           1. class TestExample(object):
          28.     
          29.       2.     def test_api_1(self):
          30.     
          31.       3.         pass
          32.     
          33.       4.   
          34.     
          35.     
          36.       5.     def test_api_2(self):
          37.     
          38.       6.         pass
          39.     
          40.       7.   
          41.     
          42.     
          43.       8.     def test_http_1(self):
          44.     
          45.       9.         pass
          46.     
          47.       10.   
          48.     
          49.     
          50.       11.     def test_http_2(self):
          51.     
          52.       12.         pass
          53.     
          54.       13.     def test_demo(self):
          55.     
          56.       14.         pass
          57.     
          58.     
          59.     

仅执行标记 API 的用例

          1. pytest -v -m api
          2.     
          3.       2. Results (0.03s):
          4.     
          5.       3.        2 passed
          6.     
          7.       4.        3 deselected
          8.     
          9.       5. 可以看到使用批量标记之后,测试用例中只执行了带有 api 的方法
          10.    
          11.     
          12.     

用例错误处理截图,App 日志等

1. 第一种使用 python 函数装饰器方法

          1.  def monitorapp(function):
          2.     
          3.       2.     """
          4.     
          5.       3.      用例装饰器,截图,日志,是否跳过等
          6.     
          7.       4.      获取系统log,Android logcat、ios 使用syslog
          8.     
          9.       5.     """
          10.    
          11.       6.   
          12.     
          13.     
          14.       7.     @wraps(function)
          15.     
          16.       8.     def wrapper(self, *args, **kwargs):
          17.     
          18.       9.         try:
          19.     
          20.       10.             allure.dynamic.description('用例开始时间:{}'.format(datetime.datetime.now()))
          21.     
          22.       11.             function(self, *args, **kwargs)
          23.     
          24.       12.             self.Action.driver.get_log('logcat')
          25.     
          26.       13.         except Exception as E:
          27.     
          28.       14.             f = self.Action.driver.get_screenshot_as_png()
          29.     
          30.       15.             allure.attach(f, '失败截图', allure.attachment_type.PNG)
          31.     
          32.       16.             logcat = self.Action.driver.get_log('logcat')
          33.     
          34.       17.             c = '\n'.join([i['message'] for i in logcat])
          35.     
          36.       18.             allure.attach(c, 'APPlog', allure.attachment_type.TEXT)
          37.     
          38.       19.             raise E
          39.     
          40.       20.         finally:
          41.     
          42.       21.             if self.Action.get_app_pid() != self.Action.Apppid:
          43.     
          44.       22.                 raise Exception('设备进程 ID 变化,可能发生崩溃')
          45.     
          46.       23.     return wrapper
          47.     
          48.     
          49.     

2. 第二种使用 pytest hook 方法 (与方法1二选一)

          1.  @pytest.hookimpl(tryfirst=True, hookwrapper=True)
          2.     
          3.       2. def pytest_runtest_makereport(item, call):
          4.     
          5.       3.     Action = DriverClient().Action
          6.     
          7.       4.     outcome = yield
          8.     
          9.       5.     rep = outcome.get_result()
          10.    
          11.       6.     if rep.when == "call" and rep.failed:
          12.     
          13.       7.         f = Action.driver.get_screenshot_as_png()
          14.     
          15.       8.         allure.attach(f, '失败截图', allure.attachment_type.PNG)
          16.     
          17.       9.         logcat = Action.driver.get_log('logcat')
          18.     
          19.       10.         c = '\n'.join([i['message'] for i in logcat])
          20.     
          21.       11.         allure.attach(c, 'APPlog', allure.attachment_type.TEXT)
          22.     
          23.       12.         if Action.get_app_pid() != Action.apppid:
          24.     
          25.       13.                 raise Exception('设备进程 ID 变化,可能发生崩溃')
          26.     
          27.     
          28.     

Pytest 另一些 hook 的使用方法

1. 自定义 Pytest 参数

          1.  > pytest -s -all
          2.     
          3.     
          4.     
          5.     
          6.           1. # content of conftest.py
          7.     
          8.       2. def pytest_addoption(parser):
          9.     
          10.      3.     """
          11.     
          12.       4.     自定义参数
          13.     
          14.       5.     """
          15.     
          16.       6.     parser.addoption("--all", action="store_true",default="type1",help="run all combinations")
          17.     
          18.       7.   
          19.     
          20.     
          21.       8. def pytest_generate_tests(metafunc):
          22.     
          23.       9.     if 'param' in metafunc.fixturenames:
          24.     
          25.       10.         if metafunc.config.option.all: # 这里能获取到自定义参数
          26.     
          27.       11.             paramlist = [1,2,3]
          28.     
          29.       12.         else:
          30.     
          31.       13.             paramlist = [1,2,4]
          32.     
          33.       14.         metafunc.parametrize("param",paramlist) # 给用例加参数化
          34.     
          35.       15.   
          36.     
          37.     
          38.       16. # 怎么在测试用例中获取自定义参数呢
          39.     
          40.       17. # content of conftest.py
          41.     
          42.       18. def pytest_addoption(parser):
          43.     
          44.       19.     """
          45.     
          46.       20.     自定义参数
          47.     
          48.       21.     """
          49.     
          50.       22.     parser.addoption("--cmdopt", action="store_true",default="type1",help="run all combinations")
          51.     
          52.       23.   
          53.     
          54.     
          55.       24.   
          56.     
          57.     
          58.       25. @pytest.fixture
          59.     
          60.       26. def cmdopt(request):
          61.     
          62.       27.     return request.config.getoption("--cmdopt")
          63.     
          64.       28.   
          65.     
          66.     
          67.       29.   
          68.     
          69.     
          70.       30. # test_sample.py 测试用例中使用
          71.     
          72.       31. def test_sample(cmdopt):
          73.     
          74.       32.     if cmdopt == "type1":
          75.     
          76.       33.         print("first")
          77.     
          78.       34.     elif cmdopt == "type2":
          79.     
          80.       35.         print("second")
          81.     
          82.       36.     assert 1
          83.     
          84.       37.   
          85.     
          86.     
          87.       38. > pytest -q --cmdopt=type2
          88.     
          89.       39. second
          90.     
          91.       40. .
          92.     
          93.       41. 1 passed in 0.09 seconds
          94.     
          95.     
          96.     

2. Pytest 过滤测试目录

          1.  #过滤 pytest 需要执行的文件夹或者文件名字
          2.     
          3.       2. def pytest_ignore_collect(path,config):
          4.     
          5.       3.     if 'logcat' in path.dirname:
          6.     
          7.       4.         return True #返回 True 则该文件不执行
          8.     
          9.     
          10.    

Pytest 一些常用方法

1. Pytest 用例优先级(比如优先登录什么的)

          1.  > pip install pytest-ordering
          2.     
          3.     
          4.     
          5.     
          6.           1. @pytest.mark.run(order=1)
          7.     
          8.       2. class TestExample:
          9.     
          10.      3.     def test_a(self):
          11.     
          12.     
          13.     

2. Pytest 用例失败重试

          1.  #原始方法
          2.     
          3.       2. pytet -s test_demo.py
          4.     
          5.       3. pytet -s --lf test_demo.py #第二次执行时,只会执行失败的用例
          6.     
          7.       4. pytet -s --ll test_demo.py #第二次执行时,会执行所有用例,但会优先执行失败用例
          8.     
          9.       5. #使用第三方插件
          10.    
          11.       6. pip install pytest-rerunfailures #使用插件
          12.     
          13.       7. pytest --reruns 2 # 失败case重试两次
          14.     
          15.     
          16.     

3. Pytest 其他常用参数

          1.  pytest --maxfail=10 #失败超过10次则停止运行
          2.     
          3.       2. pytest -x test_demo.py #出现失败则停止
          4.     
          5.     
          6.     

总结

以上,尽可能的汇总了常见的问题和好用的方法,希望对测试同学们有帮助!下一篇文章将计划讲解 用 Pytest hook 函数运行 yaml 文件来驱动
Appium 做自动化测试实战,并提供测试源码,敬请期待!

**-
来霍格沃兹测试开发学社,学习更多软件测试与测试开发的进阶技术,知识点涵盖web自动化测试 app自动化测试、接口自动化测试、测试框架、性能测试、安全测试、持续集成/持续交付/DevOps,测试左移、测试右移、精准测试、测试平台开发、测试管理等内容,课程技术涵盖bash、pytest、junit、selenium、appium、postman、requests、httprunner、jmeter、jenkins、docker、k8s、elk、sonarqube、jacoco、jvm-sandbox等相关技术,全面提升测试开发工程师的技术实力
QQ交流群:484590337
公众号 TestingStudio
视频资料领取:https://qrcode.testing-studio.com/f?from=CSDN&url=https://ceshiren.com/t/topic/15844
点击查看更多信息

你可能感兴趣的:(软件测试,测试开发,自动化测试,ui,自动化,压力测试,测试工程师)