Pytest骨灰级教学(二)前后置方法和fixture机制

我们知道,在unittest中,可以使用setUp/tearDown或者setUpClass/tearDownClass来实现函数/类级别的前后置方法,这是比较僵硬的。而在pytest中,你可以通过fixture夹具来实现前后置,也可以通过xunit风格的前后置方法来实现,xunit风格的前后置其实只是Pytest为了向下兼容unittest的前后置,为习惯unittest的测试人员使用的,但是既然使用了pytest,那我们这里还是推荐使用fixture夹具,因为相对来说fixture夹具确实更加灵活。
夹具需要使用@pytest.fixture这个装饰器来定义,pytest中的测试夹具有如下几个级别:用例级别、测试类级别、模块级别,包级别,会话级别。接下来我们一起来看看夹具定义语法。
夹具一般都是写在conftest.py文件中,写在conftest.py中的夹具,可以无需导入便在所有模块中进行调用,如果只是针对某个模块的测试用例的夹具,那么可以直接将夹具写在该模块中,本文都使用contest.py进行夹具的编写。后续教程还会涉及到conftest.py文件中的钩子(hook)函数。
一、夹具级别

"""夹具定义可以通过参数scope指定夹具的级别,如果不指定夹具级别,scope 默认值为function(用例级别)
用例级别:scope = function
测试类级:scope = class
模块级别:scope = module
包级别:  scope = package
会话级别:scope = session"""
@pytest.fixture(scope='指定夹具的级别')
def work():
    # 前置执行脚本
    yield 
    # 后置执行脚本

测试夹具本质上是一个生成器函数,生成器函数在使用next进行迭代时,执行到yeild会返回数据,暂停执行,等待下一次进行迭代时才会继续执行,pytest夹具就是利用的生成器的机制,通过yeild在测试夹具中将前后置代码分开执行。夹具只有在定义夹具的范围内才能使用。如果夹具是在类中定义的,则只能由该类内的测试用例使用。但是如果在模块的全局范围内定义的夹具,那么该模块中的每个测试用例,即使它是在一个类中定义的,都可以使用它。不过最好还是在conftest.py文件中编写夹具函数。
知道了怎么定义夹具,那么接下来我们来看看如何使用夹具。

二、夹具的使用
测试夹具定义好之后,测试用例函数通过将它们声明为参数传入,来指定执行用例要使用的夹具(即前后置)。
当 pytest 开始运行测试时,它会查看该测试函数定义的形参,然后搜索与这些参数同名的测试夹具。一旦 pytest 找到它们,它就会运行这些夹具,接收它们返回的内容(如果有的话),并将这些返回内容传递给测试用例函数。当我们使用夹具时,如果夹具的前置脚本执行完,有数据或对象要传递给测试用例,把需要传递的数据或对象写在yield后面即可,在使用夹具的用例或者方法中,可以通过定义的形参来获取yeild返回的数据或对象,下面会有实例介绍。
1、
在用例中使用夹具
不管是函数形式定义的测试用例,还是测试类中方法的形式定义的用例,在使用的时候都是一样的,直接定义一个和要使用的夹具同名的形参即可。
conftest.py:

# 定义一个用例级别的夹具
@pytest.fixture
def my_fixture():
    print('------my_fixture---用例前置执行脚本--------')
    yield
    print('------my_fixture---用例后置执行脚本--------')

test_demo,py:

# 测试用例 指定测试夹具my_fixture
def test_func__01(my_fixture):
    print("测试用例----test_func__01----")


class TestDome:
    # 函数用例 指定测试夹具
    def test_02(self, my_fixture):
        print('----测试用例:test_02------')
  
     # 函数用例 指定测试夹具
    def test_03(self):
        print('----测试用例:test_03------')

运行结果如下:

 上面test_func__01和test_02这两个用例在定义时指定了测试夹具,而test_03则没有指定执行的夹具。

2、测试类和模块指定夹具
上面我们通过给用例方法加形参来给单个测试用例指定测试夹具。如果一个测试类中有很多测试用例或者一个模块中有很多用例,都要指定同一个测试夹具,要我们则可以通过usefixtures给测试类或测试模块指定测试夹具。
2.1、给测试类中所有的用例指定夹具:

# TestDemo这个测试类的所有测试用例均执行my_fixture这个夹具
@pytest.mark.usefixtures('my_fixture')
class TestDemo:
    # 函数用例
    def test_01(self):
        print('----测试用例:test_01------')

    # 函数用例
    def test_02(self):
        print('----测试用例:test_02------')

运行结果如下:

2.2、给模块中所有的用例指定夹具:

# 当前模块中所有的用例,均执行my_fixture这个测试夹具
pytestmark = pytest.mark.usefixtures('my_fixture')

# 函数用例 指定测试夹具
def test_func__01(my_fixture):
    print("测试用例————test_func__01——————")

  
class TestDome:
    # 函数用例 指定测试夹具
    def test_01(self):
        print('----测试用例:test_01------')

    # 函数用例 指定测试夹具
    def test_02(self):
        print('----测试用例:test_02------')

运行结果如下:

 3、在夹具中引用夹具
pytest 的最大优势之一是其极其灵活的夹具系统。通过测试夹具我们可以将极为复杂化的前后置依赖,拆分为更简单单一功能的测试夹具,通过在夹具中引用其他的夹具,来组织不同用例所需的复杂依赖。接下来通过一个案例来给大家演示如何使用。
conftest.py:

import pytest
# 用户注册的夹具
@pytest.fixture()
def register_user():
    print('---用户注册的夹具前置执行----')
    # ...注册代码省略,注册的用户信息如下
    user_info = {'user': 'qa123', 'pwd': '123456'}
    yield user_info
    print('---用户注册的夹具后置执行----')


# 用户登录的夹具,通过定义形参来使用register_user这个夹具,也就是说这里每次都会注册一个新用户进行登录
@pytest.fixture()
def user_login(register_user):
    print('---用户登录的夹具前置执行----')
    # 获取register_user夹具前置脚本执行完,yield传递返回过来的数据(user_info)
    user_info = register_user
    print(f'注册的用户信息为{user_info}')
    # ...登录代码省略,下面为登录得到的token
    token = 'sfsaeqsfwt'
    yield token
    print('---用户登录的夹具后置执行----')

test_demo4.py:

# 函数用例 指定使用夹具user_login
def test_func__01(user_login):
    token = user_login
    print("夹具user_login传递返回过来的token:",token)
    print("测试用例---test_func__01---")

 运行结果如下:

4、自动使用夹具
定义测试夹具 我们可以给夹具的装饰器加参数autouse=True来使夹具成为自动执行的夹具。
conftest.py:

@pytest.fixture(autouse=True)
def my_fixture():
    print('------my_fixture---前置执行脚本--------')
    yield
    print('------my_fixture---后置执行脚本--------')

test_demo4.py:

class TestDemo1:
    # 函数用例 指定测试夹具
    def test_01(self):
        print('----测试用例:test_01------')

    # 函数用例 指定测试夹具
    def test_02(self):
        print('----测试用例:test_02------')


class TestDemo2:
    # 函数用例 指定测试夹具
    def test_03(self):
        print('----测试用例:test_03------')

 运行结果如下:

从上面的执行结果我们可以看到,每条用例执行之前都自动执行了测试夹具my_fixture,自动使用的夹具一般都是不带返回值的夹具。

至此,关于Pytest夹具fixture的一些用法就说完了,下面结合Selenium来实战一下代码。
conftest.py:

import pytest
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
 
@pytest.fixture(scope="function")
# scope定义该夹具的有效级别,可以理解成fixture的作用域,默认不写:function,还有class、module、package、session四个【常用】
def browser():
    """管理浏览器的启动和停止"""
    # 先设置一下浏览器的加载策略属性
    capabilities = Options().to_capabilities()
    capabilities["pageLoadStrategy"] = "eager"
    # 得到一个浏览器对象
    driver = webdriver.Chrome(desired_capabilities=capabilities)
    # 隐性等待
    driver.implicitly_wait(8)
    driver.maximize_window()
    yield driver  # yield上面的为前置操作,下面的为后置操作,yield后面的driver是返回值(对象),可以直接传入测试用例函数里
    driver.quit()

test_login.py :

def test_login(browser):# 无需导入夹具browser,可直接当作参数传入
    url = 'http://xxxxxxxxx'
    username = '123'
    password = '321'
    expected = '登陆失败'
 
    driver = browser # 使用夹具名可以直接获得夹具browser返回的driver浏览器对象
 
    # 2、访问测试网页
    driver.get(url)
 
    # 3、点击登录按钮
    login_el = driver.find_element('xpath', "//a[text()='登录']")
    login_el.click()
 
    # 4、定位用户名输入框,并输入用户名
    username_el = driver.find_element('xpath', "//input[@placeholder='请输入手机号/用户名']")
    username_el.send_keys(username)
 
    # 5、定位密码输入框,并输入密码
    password_el = driver.find_element('xpath', "//input[@placeholder='请输入密码']")
    password_el.send_keys(password)
 
    # 6、点击登录
    login_btn = driver.find_element('xpath', "//a[@class='xxxx']")
    login_btn.click()
 
    # 7、断言
    actual = driver.find_element('xpath', "//div[@class='xxxxx']")
    assert expected == actual.text

ps:通过夹具返回浏览器对象是web自动化很实用的一个技巧。

posted @ 2022-08-31 14:19  少年不太冷2  阅读(271)  评论(0)    收藏  举报