pytest测试框架
1、pytest基本介绍
pytest是一个功能强大且易于使用的python测试框架,用于编写单元测试、集成测试和功能测试。
提供了丰富的功能和灵活的用法,使得编写和运行测试边得简单而高效。
1.1 pytest框架优点
- 简单易用:pytest语法非常简洁清晰,对于编写测试用例来说,非常友好,几乎可以在几分钟内上手。
- 自动发现测试:pytest能够自动发现项目中的测试文件和测试函数,无需手动编写繁琐的配置。
- 丰富的断言库:pytest内置了丰富的断言库,可以轻松地进行测试结果的判断。
- 支持参数化测试:pytest支持参数化测试,能够快速地对多组输入进行测试。
- 插件丰富:pytest有着丰富的插件生态系统,可以通过插件扩展各种功能,比如覆盖率测试,测试报告生成等。
1.2 安装
# 安装
pip install pytest
# 查看版本
pytest --version
2、pytest基本使用
2.1 pytest的组成部分
pytest由两部分组成:
① 用例主体部分(通常单独放在一个py文件):主体部分写测试用例
② 用例运行语句(通常放在一个main文件):执行测试用例
2.2 pytest用例规范
1) 文件:必须以test_开头或_test结尾
2) 类:以Test开头,或者继承unittest.TestCase的任意名称的类
3) 方法:以test_开头的方法
4) 注意:测试类中,不可以添加init()构造函数,pytest不识别init()的类内的测试方法
2.3 pytest的运行方式
# 主函数运行方式
import pytest
if __name__ == "__main__":
pytest.main()
# 指定文件运行
pytest.main(["./testcase/test_01.py"])
# 指定目录运行
pytest.main(["./testcase"])
# 指定运行的测试类中的用例
pytest.main(["./testcase/test_01.py"::TestDemo])
# 指定运行的测试类中的某一条用例
pytest.main(["./testcase/test_01.py"::TestDemo::test_01])
# 匹配包含关键词的用例(匹配目录名、模块名、类型、用例名)
# -k demo表示匹配包含demo的用例,可以使用and、or
pytest.main(['-k', 'demo'])
# -v显示详细信息,输出调试信息
pytest.main(['-v', './testcase'])
# -s输出更详细的信息,如:文件名、用例名
pytest.main(['-vs', './testcase'])
# -x 只要有一个用例执行失败,就停止执行测试
pytest.main(['-vsx', './testcase'])
# --maxfail 出现N个测试用例失败,就停止测试
pytest.main(['-vs', '--maxfail=3', './testcase'])
# --html=report.html 生成html格式的报告
pytest.main(['-vsx', '--html=./report/report.html', './testcase'])
3、pytest夹具 fixture
pytest框架中,fixture是一个强大的功能,用于在测试执行过程中提供固定的测试环境或数据。这使得测试更加模块化、可重用、并且易于维护。
3.1 什么是fixture
在pytest中,fixture是一个函数,它在测试执行之前被调用,用于设置测试所需的环境和数据,例如:可以使用fixture来创建一个数据库连接、加载测试数据或者模拟外部服务。
前置:环境准备、数据准备
后置:清理工作、数据清理
3.2 pytest的fixture实现方式
(1)xunit-style
跟unittest框架机制非常相似,即setup/teardown
# 1) 测试函数/方法级别
每一个测试函数都会执行的前置和后置
# 测试类内部的测试方法,模块
前置函数:setup_method
后置函数:teardown_method
# 模块下的测试函数
前置函数:setup_function
后置函数:teardown_function
# 2) 测试类级别
一个测试类只执行一次前置和后置
前置函数:setup_class
后置函数:teardown_class
# 注意:用@classmethod装饰
# 3) 模块级别
一个测试模块只执行一次前置和后置
前置函数:setup_module
后置函数:teardown_module
def setup_method():
print("模块级别setup操作")
def teardown_method():
print("模块级别teardown操作")
def setup_function():
print("函数级别setup操作")
def teardown_function():
print("函数级别teardown操作")
class TestSys:
@classmethod
def setup_class():
print("类级别setup操作")
@classmethod
def teardown_class():
print("类级别teardown操作")
(2)fixture机制
通过@pytest.fixture装饰器来定义fixture
- fixture通过函数来实现
- 使用@pytest.fixture进行装饰
- 前置和后置都放在同一个函数当中,通过yield关键字区分前置和后置,yield前面的为前置,yield后面的为后置
import pytest
@pytest.fixture
def init_test():
print("测试前置代码")
yield
print("测试后置代码")
@pytest.fixture
def init_test02():
print("没有yeild关键字,这段代码在用例执行之前运行")
@pytest.fixture
def init_test03():
yield
print("这段代码在测试用例执行之后执行")
3.3 fixture的作用域
fixture有4个作用域
测试会话 session
测试模块 module
测试类 class
测试用例 function
设置fixture的作用域,通过@pytest.fixture(scope=作用域)来设置,默认情况下,scope=function
取值 | 范围 | 说明 |
---|---|---|
function | 函数级 | 每个函数或方法都会调用 |
class | 类级别 | 每个测试类只运行一次 |
module | 模块级 | 每个.py文件只调用一次 |
package | 包级 | 每一个python包只调用一次 |
session | 会话级 | 每次会话只需要运行一次,会话内所有方法及类、模块都共享这个方法 |
3.3 fixture的返回值
在测试用例中,要使用fixture生成数据时,需要fixture返回数据,使用yield进行返回
import pytest
from selenium import webdriver
# 类级别fixture,只执行一次fixture
@pytest.fixture(scope="class")
def init_test():
print("类级别fixture,执行所有用例之前,执行该代码")
driver = webdriver.Chrome()
# 返回driver对象
yield driver
print("类似teardown,测试用例全部执行完后,执行该代码")
driver.quit()
3.4 conftest共享机制
# 共享实现
1) 在项目根目录下,创建一个conftest.py文件
2) 文件名必须是conftest.py,大小写敏感,不可改名字
3) conftest.py当中,可以编写多个fixture
4) 在测试用例文件当中,不需要引入conftest.py文件,直接调用fixture的函数名 ,会自动去conftest.py文件中查找
conftest.py层级作用域
项目根目录下的conftest.py,作用域是整个项目,项目中所有的测试用例,都可以使用conftest.py中定义的fixture
子目录里的conftest.py,只有在子目录中的测试用例可以使用
测试用例执行时,调用的fixture的顺序,按就近原则调用
测试用例文件中的fixture > 当前目录中的fixture > 上级目录中的fixture > 根目录中的fixture
3.5 fixture的嵌套
嵌套的fixture允许在创建一个fixture时候,本身依赖另外一个fixture,这样可以构建出更加复杂和模块化的测试环境
定义嵌套fixture
嵌套fixture通常在测试用例中通过依赖注入的方式使用
# 1) 定义基础fixture
import pytest
@pytest.fixture
def base_setup():
print("我是基础的fixture,前置处理")
yield # yield语句之后的内容,在测试完成后执行
print("我是基础的fixture,后置处理")
# 2) 定义嵌套fixture
# 当前定义的fixture,可以依赖上面的基础fixture
def next_setup(base_setup):
print("我是嵌套的fixture,前置处理")
yield # yeild语句之后的内容,在测试完成后执行
print("我是嵌套的fixture,后置处理")
# 3) 在测试用例中,使用嵌套fixture
def test_with_demo_01(next_setup):
print("测试用例代码")
# 以上执行顺序
① base_setup的执行,基础设置
② next_setup的执行,嵌套设置
③ 测试用例执行 test_with_demo_01
④ next_setup的情况,嵌套清理
⑤ base_setup的清理,基础清理
3.6 fixture的优势
- 减少重复代码:通过将公共的资源或环境初始化放在fixture中,可以避免在每个测试用例中重复编写相同的代码
- 提高代码可读性:通过将测试用例的依赖项以参数的形式传入,使得测试用例的意图更加明确,便于阅读和维护
- 便于扩展和维护:通过将复杂的测试环境或资源抽象为fixture,使得后续的扩展和维护变更更加容易;可以根据需要添加更多的fixture来满足不同的测试需求
- 支持依赖注入:fixture不仅可以为测试用例提供数据和环境,还可以支持依赖注入。这样可以将一个对象或服务的实例传递给需要它的多个测试用例
数据库连接:在测试数据库相关的操作时,我们通常需要先建立数据库连接。通过使用fixture,可以确保每个测试用例都有干净的数据库环境
模拟数据:在测试过程中,我们可能需要模拟一些数据来满足特定的条件。通过fixture,我们可以轻松地创建和初始化模拟数据
配置初始化:对于一些配置相关的测试,我们可以使用fixture来初始化配置文件或环境变量,这样每个测试用例都可以基于相同的配置进行验证
外部服务连接:当测试设计到外部服务时,我们可以使用fixture来建立与服务器之间的连接,并在测试结束后关闭连接
依赖注入:对于一些复杂的对象或服务,我们可以将其实例化并作为fixture传递给需要它的测试用例,这样,不同的测试用例可以共享一个示例,而无需在每个测试中重新创建