系列文章目录

第一章 Pytest单元测试框架基础

第二章 Pytest单元测试框架之fixture装饰器实现前后置


目录

系列文章目录

前言

一、Pytest 的setup和teardown前后置实战

二、fixture装饰器

三、conftest.py文件


前言

前面一篇讲到用例加setup和teardown可以实现在测试用例之前或之后加入一些操作,但这种是整个脚本全局生效的,如果我想实现以下场景: 用例1需要先登录,用例2不需要登录,用例3需要先登录。很显然这就无法用setup和teardown来实现了。这就是本篇学习的目的,自定义测试用例的预置条件

Pytest提供了fixture机制,通过它可以在测试执行前后执行一些操作,类似setup和teardown。


一、Pytest 的setup和teardown前后置实战

上一篇讲到Pytest 的setup和teardown前后置的使用,本篇带你进行项目实战。

由于本章节主要讲的是fixture装饰器,所以我们主要以类级别与函数级别进行实战:

1、类级别前后置:

# 导入所需要的包
import pytest
from selenium import webdriver
from time import sleep
from selenium.webdriver.common.by import By

class Test_login:
    # 类级前置操作
    def setup_class(self):
        print('------>执行前置操作')
        # 加载浏览器驱动
        self.driver = webdriver.Chrome()
        # 网页全屏
        self.driver.maximize_window()
        # 打开电商商城登录页
        self.driver.get("https://www.KameyMall.com/login")
    # 类级后置操作
    def teardown_class(self):
        print('------>执行后置操作')
        # 用例执行完成后等待3秒
        sleep(3)
        # 退出浏览器
        self.driver.quit()

    def test_login(self):
        print('用例01')
        # 输入账号
        self.driver.find_element(By.ID, 'fm-login-id').send_keys('*********')
        sleep(2)
        # 输入密码
        self.driver.find_element(By.XPATH, '//*[@id="fm-login-password"]/div/input').send_keys('********')
        sleep(2)
        # 点击登录按钮
        self.driver.find_element(By.CSS_SELECTOR, 'button[class="fm-button"]').click()
        sleep(5)

    def test_order(self):
        print('用例02')
        # 进入订单页面
        self.driver.find_element(By.LINK_TEXT, 'Orders').click()
        # 点击所有订单
        self.driver.find_element(By.LINK_TEXT, 'My Kameymall').click()

if __name__ == '__main__':
    pytest.main(['-vs'])

注意!由于我安装的是selenium4版本,所以定位的写法不同!

selenium3 :driver.find_element_by_id('元素')

selenium4 :driver.find_element(By.ID,'元素')

运行结果:

pytest国内镜像_单元测试

因为调用的是类级别的前后置,我的代码是有一个Test_login类,类里面有两条用例,所以执行前置操作后执行了两个用例再执行后置操作。

注意!如果用例2的元素不在用例1最后进入的页面则会执行失败,所以必须保证用例1登陆后进入的页面存在用例2的元素!

 2、函数级别前后置:

# 导入所需要的包
import pytest
from selenium import webdriver
from time import sleep
from selenium.webdriver.common.by import By

class Test_login:
    # 类级前置操作
    def setup(self):
        print('------>执行前置操作')
        # 加载浏览器驱动
        self.driver = webdriver.Chrome()
        # 网页全屏
        self.driver.maximize_window()
        # 打开电商商城登录页
        self.driver.get("https://www.KameyMall.com/login")
    # 类级后置操作
    def teardown(self):
        print('------>执行后置操作')
        # 用例执行完成后等待3秒
        sleep(3)
        # 退出浏览器
        self.driver.quit()

    def test_login(self):
        print('用例01')
        # 输入账号
        self.driver.find_element(By.ID, 'fm-login-id').send_keys('********')
        sleep(2)
        # 输入密码
        self.driver.find_element(By.XPATH, '//*[@id="fm-login-password"]/div/input').send_keys('********')
        sleep(2)
        # 点击登录按钮
        self.driver.find_element(By.CSS_SELECTOR, 'button[class="fm-button"]').click()

    def test_order(self):
        print('用例02')
        # 输入账号
        self.driver.find_element(By.ID, 'fm-login-id').send_keys('**********')
        sleep(2)
        # 输入密码
        self.driver.find_element(By.XPATH, '//*[@id="fm-login-password"]/div/input').send_keys('*******')
        sleep(2)
        # 点击登录按钮
        self.driver.find_element(By.CSS_SELECTOR, 'button[class="fm-button"]').click()
        sleep(5)
        # 进入订单页面
        self.driver.find_element(By.LINK_TEXT, 'Orders').click()
        # 点击所有订单
        self.driver.find_element(By.LINK_TEXT, 'My Kameymall').click()

if __name__ == '__main__':
    pytest.main(['-vs'])

运行结果:

pytest国内镜像_pytest国内镜像_02

可以清晰的看到使用函数级别的前后置,每个用例都会执行一次。这里由于用例2需要先登录才能进入订单页面,所以我把登录操作也写进了用例2。接下来我们讲fixture装饰器。 

二、fixture装饰器

1、fixture优势

firture相对于setup和teardown来说应该有以下几点优势:

  • 命名方式灵活,不局限于setup和teardown这几个命名
  • conftest.py 配置里可以实现数据共享,不需要import就能自动找到配置
  • scope="module" 可以实现在当前模块.py文件前后调用一次
  • scope="session" 以实现多个.py跨文件使用一个session来完成多个用例

@pytest.fixture函数的scope可能的取值有function,class,module,package 或 session。他们的具体含义如下:

function,表示fixture函数在测试方法执行前和执行后执行一次。 class,表示fixture函数在测试类执行前和执行后执行一次。 module,表示fixture函数在测试脚本执行前和执行后执行一次。 package,表示fixture函数在测试包(文件夹)中第一个测试用例执行前和最后一个测试用例执行后执行一次。 session,表示所有测试的最开始和测试结束后执行一次。

@pytest.fixture装饰的函数的函数名可以作为测试方法的参数,在测试方法中,使用fixture函数名作为变量,就相当于是在调用fixture装饰的函数。

fixture(scope="function", params=None, autouse=False, ids=None, name=None) @pytest.fixture()如果不写参数,默认就是scope="function",它的作用范围是每个测试用例执行之前运行一次 如果autouse为True,则为所有测试直接激活fixture, 无需往每个函数传入fixture就可以调用它。 如果为False(默认值),则需要往测试函数传入fixture标记的函数名。

2、fixture装饰器的使用

这里主要讲fixture装饰器的scope、autouse、name。

scope :作用域(模块、类、函数。。。),不填则默认使用函数级。

autouse : False是手动调用,True是自动调用。

name : 输入名称用于用例调用(用于fixture手动调用)。

这里以类级示例:

import pytest

# 类级
@pytest.fixture(scope='class', autouse=True)  # 自动调用
def web_aa():
    print('------>前置操作')
    yield
    print('------>后置操作')

class Test_aaaa:
    def test_login(self):
        print('用例01')

    def test_order(self):
        print('用例02')

if __name__ == '__main__':
    pytest.main(['-vs'])

运行结果:

pytest国内镜像_python_03

 可以看到运行结果与setup和teardown函数是一样的效果。

手动调用:

import pytest

# 类级
@pytest.fixture(scope='class', autouse=False, name='aaa')  # 手动调用
def web_aa():
    print('------>前置操作')
    yield
    print('------>后置操作')

# 手动调用的方式:@pytest.mark.usefixtures('aaa') 因为我们定义了name = aaa,没有定义name可以用web_aaa
@pytest.mark.usefixtures('aaa')
class Test_aaaa:
    def test_login(self):
        print('用例01')

    def test_order(self):
        print('用例02')

if __name__ == '__main__':
    pytest.main(['-vs'])

运行结果:

pytest国内镜像_自动化_04

 与自动调用的结果是一样的。模块级别与函数级别是一样的调用方式。

三、conftest.py文件

  • 可以跨.py文件调用,有多个.py文件调用时,实际只调用了一次fixture。
  • conftest.py与运行的用例要在同一个 pakage 下,并且有__init__.py文件。
  • 不需要 import 导入conftest.py,pytest 会自动识别该文件,放到项目的根目录下就可以全局调用了,如果放到某个 package 下,那就在改 package 内有效。
  • conftest.py配置脚本名称是固定的,不能改名称。

创建conftest.py文件,讲上面的fixtrue代码放进conftest.py文件,我们现在要做的是将conftest.py文件里的前后置调用到用例文件。

# conftest.py文件

import pytest

@pytest.fixture(scope='class', autouse=False, name='aaa')
def web_aa():
    print('------>执行前置操作')

    yield
    print('------>后置操作')
# 用例文件

import pytest

@pytest.mark.usefixtures('aaa')  # 通过定义的name值调用
class Test_aaaa:
    def test_login(self):
        print('用例01')

    def test_order(self):
        print('用例02')

if __name__ == '__main__':
    pytest.main(['-vs'])

执行结果:

pytest国内镜像_自动化_05