场景一:
假设每个测试用例都需要在登录的前提下,或者有个别的用例不需要登录,当你的项目中用例非常多的时候,难道需要每条用例都去setup一遍登录的功能吗?这显然是不实际的!

场景二:
假设我觉得每跑一条用例都执行一次登录账号,再关闭浏览器,用例集一多,这会显得特别的浪费时间,我想执行一个模块就只登录一次,关闭浏览器一次,甚至于我整个过程就希望只登录一次,这对于UI自动化来说,是不是更加”人性化”?

别急 你要的 pytest都能满足你!
首先我们来了解下pytest的fixture之conftest.py文件

conftest.py

coonftest.py是pytest特有的本地测试配置文件,可以用来设置项目级别的FixTure,可以用来导入外部插件、还可以用来指定钩子函数

  1. def fixture(callable_or_scope: Any = None,
  2. *args: Any,
  3. scope: str = "function",
  4. params: Any = None,
  5. autouse: bool = False,
  6. ids: Any = None,
  7. name: Any = None) -> Union[(args: Tuple[Any, ...], kwargs: Dict[str, Any]) -> None, FixtureFunctionMarker]

fixture参数scope:
session:同一个会话范围只调用一次
module:每个.py文件调用一次,可包含多个测试用例
class::每个类调用一次
_
fixture参数autouse:为True时则自动调用,例如:pytest.fixture(scope=”function”, autouse=True),则每次执行function时先调用


fixture配置注意事项

conftest.py必须与要执行的用例在同一个pakage下,仅对当前pakage包下文件生效,并且init.py文件必不可少

fixture调用方式

1.定义钩子函数,即直接传fixture的函数名称

实例:

  1. # 定义基本测试环境
  2. @pytest.fixture(scope='session')
  3. def myapp_url():
  4. return RunConfig.myapp_url
  5. def test_update_h5_desc(self, browser, myapp_url):
  6. """1.在作品管理列表点击转发按钮
  7. 2.修改作品描述
  8. 3,点击保存"""
  9. page = H5_Manage(browser)
  10. page.get(myapp_url)
  11. page.click_share.click() # 点击转发按钮
  12. page.set_desc.select_all() # 全选文本
  13. page.set_desc.delete() # 清除文本
  14. page.set_desc.send_keys("zaygee自动化测试修改作品描述")
  15. page.click_save.click() # 保存修改
  16. text = page.toast.get_attribute('textContent') # 获取toast提示信息
  17. assert '保存成功' in text


2.使用装饰器pytest.mark.usefixtures()修饰

语法:pytest.mark,usefixtures(“装饰函数名”)

3.设置autouse = True 自动调用

如下代码,每个.py文件都自动调用该装饰函数

  1. @pytest.fixture(scope='module', autouse=True)
  2. def test_tuzhan_login(browser, base_url):
  3. """登录用例:
  4. 1.点击登录按钮
  5. 2.点击账号,输入账号、密码
  6. 3.点击登录"""
  7. page = Login(browser)
  8. page.get(base_url)
  9. page.login_button.click()
  10. page.into_frame.switch_to_frame()
  11. page.login_pop_window.click()
  12. page.input_user.send_keys(tz_username)
  13. page.input_pw.send_keys(tz_password)
  14. page.login_click.click()
  15. page.switch_to_parent_frame()
  16. time.sleep(3)
  17. assert browser.title == '工作台-XX'
  18. return page

fixture中实现teardown

背景:
上文所述fixture的应用,都是在用例执行之前,相当于是setup的动作,那么fixture是否能实现teardown的功能呢? 答案肯定是可以的,借助yield的强大功能yield详解

场景:UI自动化中,要实现初始化浏览器并在用例执行完成后执行关闭浏览器的一系列动作,
相当于setup动作>执行用例动作>teardown动作

实例:
如下代码 ,实现的是conftest.py文件中定义全局浏览器驱动,当在用例文件中引用该修饰函数,
执行到yield driver,则会暂时”暂停”并执行用例文件,等到用例执行完毕,最后执行driver之后的代码,达到关闭浏览器的效果!

  1. # conftest.py
  2. # 启动浏览器并关闭
  3. @pytest.fixture(scope='module', autouse=True)
  4. def browser():
  5. """定义全局浏览器驱动"""
  6. global driver
  7. if RunConfig.driver_type == 'chrome':
  8. """本地chrome浏览器"""
  9. driver = webdriver.Chrome()
  10. driver.maximize_window()
  11. elif RunConfig.driver_type == 'chrome_headless':
  12. """chrome headless模式"""
  13. chrome_options = CH_Options()
  14. chrome_options.add_argument('--headless')
  15. chrome_options.add_argument('--disable-gpu')
  16. chrome_options.add_argument('--window-size=1920x1080')
  17. driver = webdriver.Chrome(options=chrome_options)
  18. elif RunConfig.driver_type == "grid":
  19. driver = Remote(command_executor='http://localhost:4444/wb/hub',
  20. desired_capabilities={
  21. "browserName": 'chrome',
  22. })
  23. driver.set_window_size(1920, 1080)
  24. else:
  25. raise NameError("driver浏览器驱动定义错误!")
  26. yield driver
  27. # # 清除浏览器缓存
  28. # driver.get("chrome://settings/clearBrowserData")
  29. # driver.find_element_by_xpath('//settings-ui').send_keys(Keys.ENTER)
  30. driver.quit()
  31. print("test end!!!!!!!!!!!!")

fixture的工厂化

当自定义由fixture装饰的函数需要被多次调用且可传递参数时,可考虑使用闭包函数

  1. import pytest
  2. @pytest.fixture()
  3. def test_with_args():
  4. def _with_args(args):
  5. print(f'成功带上参数了:{args} ')
  6. return args
  7. return _with_args
  8. class TestFixtureWithArgs:
  9. def test_1(self, test_with_args):
  10. print('测试fixture自定义钩子函数带参数')
  11. test_with_args('Args')
  12. """
  13. test_fixture_with_args.py::TestFixtureWithArgs::test_1 PASSED [100%]
  14. 测试fixture自定义钩子函数带参数
  15. 成功带上参数了:Args
  16. ============================== 1 passed in 0.01s ===============================
  17. """

fixture的参数化

其中params为可设置的参数列表,ids为用例别名,fixture的参数化,通过request.param传递

  1. @pytest.fixture(params=["test-1", "test-2"], ids=["case-1", "case-2"])
  2. def test_with_args(request):
  3. def _with_args(args):
  4. print(f'成功带上参数了:{args} {request.param}')
  5. return args
  6. return _with_args
  7. class TestFixtureWithArgs:
  8. def test_1(self, test_with_args):
  9. print('测试fixture自定义钩子函数带参数')
  10. test_with_args('Args')
  11. """
  12. test_fixture_with_args.py::TestFixtureWithArgs::test_1[case-1]
  13. test_fixture_with_args.py::TestFixtureWithArgs::test_1[case-2]
  14. PASSED [ 50%]
  15. 测试fixture自定义钩子函数带参数
  16. 成功带上参数了:Args test-1
  17. PASSED [100%]
  18. 测试fixture自定义钩子函数带参数
  19. 成功带上参数了:Args test-2
  20. """