网络知识 娱乐 Python单元测试框架pytest

Python单元测试框架pytest

前提

pytest是一个非官方的单元测试框架,需要先进行安装。所以pip一下

技术点

一、运行参数(进入到相应目录)

1、无参数运行

运行目录下的所有py文件:pytest
运行目录下某一个py文件:pytest test_01.py
运行目录下py文件中的某个类:pytest test_02.py::TestClass
运行目录下py文件中某个类的某个方法:pytest
test_02.py::TestClass::test_one
指定目录运行:pytest testpy

2、-v参数

打印详细的日志信息

3、-s参数

代码里面有 print 输出语句,想在运行结果中打印 print 输出

4、-k参数

pytest -k '类名' //运行指定的类
pytest -k '方法名' //运行指定的方法
pytest -k '类名 and not 方法名' //运行类里的方法,不包含某个方法

5、-x参数

遇到失败用例立即停止运行

6、--maxfail参数

用例失败个数达到阀值后停止运行
pytest --maxfail 2 test_02.py

7、-m参数

只运行标记 @pytest.mark.[标记名]的方法和类

比如类名上添加:

@pytest.mark.oneone,

执行命令:pytest -m "oneone" test_02.py

如果有多个标记可以用 and 或 or 进行组合

8、--durations参数

获取执行最慢的一个:pytest --durations=1

9、--collect-only参数

只收集用例不执行,可用于统计用例数

二、pytest框架结构

1 import pytestn 2 n 3 def setup_module():n 4 print("nsetup_module,只执行一次,当有多个测试类的时候使用")n 5 def teardown_module():n 6 print("nteardown_module,只执行一次,当有多个测试类的时候使用")n 7 n 8 n 9 # setup_function teardown_function作用于类外的函数n10 def setup_function():n11 print("nsetup_function")n12 def teardown_function():n13 print("nsetup_function")n14 n15 def test_cls_out():n16 print("类外的函数方法")n17 n18 n19 class TestPytest1:n20 def setup_class(self):n21 print("nsetup_class1,只执行一次,当有多个测试方法的时候使用")n22 def teardown_class(self):n23 print("nteardown_class1,只执行一次,当有多个测试方法的时候使用")n24 n25 n26 def setup_method(self):n27 print("nsetup_method1,每个测试方法都执行一次")n28 def teardown_method(self):n29 print("teardown_method1,每个测试方法都执行一次")n30 n31 n32 def setup(self):n33 print("setup")n34 def teardown(self):n35 print("teardown")n36 n37 n38 def test_three(self):n39 print("test_three,测试用例")n40 n41 def test_four(self):n42 print("test_four,测试用例")

三、控制执行顺序

安装:pip install pytest-ordering
负数越小越先执行(-100,-18,-1)
正数越小越先执行(1,18,100)

1 import pytestn 2 n 3 n 4 class Testpy:n 5 @pytest.mark.run(order=1)n 6 def test_one(self):n 7 print(111)n 8 n 9 @pytest.mark.run(order=18)n10 def test_two(self):n11 print(222)n12 n13 @pytest.mark.run(order=100)n14 def test_three(self):n15 print(333)

四、并发执行

安装:pip install pytest-xdist
多个CPU并行执行用例,如果参数为 auto 自动检测系统的 CPU 数目;如果参数为数字,则指定运行测试的处理器进程数。
pytest -n auto
pytest -n [num]

五、pytest-html 生成测试报告

安装:pip install pytest-html
指定报告的存放路径
--html=./report/report.html
加这个参数生成的报告css不是独立的
--self-contained-html

六、assert断言

assert a
assert a == b
assert a in b
assert not a
assert a != b (a <> b一般不再使用)

七、@pytest.fixture

fixture 有一个参数 scope,通过 scope 可以控制 fixture 的作用范围,根据作用范围大小划分:session> module> class> function
具体作用范围如下:
  function 函数或者方法级别都会被调用(默认)
  class 类级别调用一次
  module 模块级别调用一次
  session 是多个文件调用一次(可以跨.py文件调用,每个.py文件就是module)

1、以参数的形式传入到方法里执行

1 import pytestn 2 n 3 @pytest.fixture()n 4 def login():n 5 print("登录需要的操作步骤")n 6 n 7 @pytest.fixture()n 8 def operate():n 9 print("用例的执行步骤")n10 n11 @pytest.fixture()n12 def xiao():n13 print("1234567890")n14 n15 # 需要在函数中传入函数名,函数上需要先标记上 @pytest.fixture()n16 def test_case1(login, operate, xiao):n17 print("test_case1,需要登录执行完毕")

2、fixture的两种引用方式

1 import pytestn 2 n 3n 4 @pytest.fixture()n 5 def open():n 6 print("打开浏览器")n 7 yieldn 8 n 9 print("执行teardown !")n10 print("最后关闭浏览器")n11 n12 n13 # 方式一:装饰器方式引用n16 @pytest.mark.usefixtures("open")n17 def test_search1():n18 print("test_search1")n19 # raise NameErrorn20 passn21 n22 n23 # 方式二:在函数中传入n26 def test_search2(open):n27 print("test_search2")n28 # raise NameErrorn29 pass

3、conftest.py应用
conftest.py等同于scope=session
pytest test_scope1.py test_scope2.py

相应代码见:
https://github.com/hanshoukai/pytest_fixture

fixture为function

Python单元测试框架pytest

fixture为class

Python单元测试框架pytest

fixture为module

Python单元测试框架pytest

fixture为session

Python单元测试框架pytest

4、自动执行fixture装饰下的函数
conftest.py中标记为:@pytest.fixture(autouse="true")

每个测试函数都会自动调用该前置函数


5、fixture传递参数

1 import pytestn2 n3 @pytest.fixture(params=[1, 2, 3])n4 def params(request):n5 return request.paramn6 n7 def test_com(params):n8 print(f"测试数据:{params}")n9 assert params < 5

八、@pytest.mark.parametrize参数化

传一个参数

1 # 传一个参数n2 @pytest.mark.parametrize("user", ["13552977251", "13552554252"])n3 def test_user(user):n4 print(user)

传两个参数

@pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+5", 7), ("7*5", 35)])n如上 "test_input,expected" 可以修改为 列表或者元组 的形式n列表:@pytest.mark.parametrize(["test_input","expected"], [("3+5", 8), ("2+5", 7), ("7*5", 35)])n元组:@pytest.mark.parametrize(("test_input","expected"), [("3+5", 8), ("2+5", 7), ("7*5", 35)])

1 # 传两个参数n2 @pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+5", 7), ("7*5", 35)])n3 def test_function(test_input, expected):n4 # eval可以将字符串转成3+5的表达方式n5 print(test_input, expected)n6 assert eval(test_input) == expected

传三个参数 三组数据,ids是别名必须与参数的数量保持一致

参数组合

1 # 参数组合n2 @pytest.mark.parametrize("x", [1, 2])n3 @pytest.mark.parametrize("y", [3, 4, 5])n4 def test_num(x, y):n5 # print(f"测试数据组合x: {x} , y:{y}")n6 print("测试数据组合x:"+str(x)+" y:"+str(y))

函数返回值类型

1 # 函数返回值类型n2 def return_data():n3 return [(1, 2), (3, 4)]n4 n5 @pytest.mark.parametrize("a,b", return_data())n6 def test_data(a, b):n7 print(a)n8 print(b)

用yaml文件做为参数化的数据源

companyid.yaml

-n - 23725503n - 24721214n - 2352987806

data.yaml

-n - 20n - 30n-n - 40n - 50

脚本:

1 import pytestn 2 import yamln 3 n 4 n 5 @pytest.mark.parametrize('a, b', yaml.safe_load(open("data.yaml", encoding='utf-8')))n 6 # @allure.step("方法的描述信息")n 7 def test_fo(a, b):n 8 print(a)n 9 print(b)n10 n11 n12 @pytest.mark.parametrize('company_id', yaml.safe_load(open("companyid.yaml", encoding='utf-8')))n13 # @allure.step("方法的描述信息")n14 def test_foo(company_id):n15 print("企业ID:", company_id)

九、pytest.ini模板

1 [pytest]n 2 # 空格分隔,可添加多个命令行参数 重试两次每隔5秒 生成报告n 3 addopts = -sv --reruns 2 --reruns-delay 5 --html=./report/report.html --self-contained-htmln 4 #addopts = -s -v --alluredir=./resultn 5 # 当前目录下n 6 testpaths = ./testcase/n 7 #配置测试搜索的文件名称,当前目录下以test开头,以.py结尾的所有文件n 8 python_files = test*.pyn 9 #配置测试搜索的测试类名,当前目录下以Test开头的所有类n10 python_classes = Testn11 #配置测试搜索的测试类名,当前目录下以test_开头的所有方法n12 python_functions = test_*

十、更多插件

用例失败后自动重新运行:pytest-rerunfailures

使用方法:pytest test_x.py --reruns=2 --reruns-delay 5 #失败后重试2次,每次间隔5秒

在脚本中指定定义重跑的次数,这个时候在运行的时候,就无需加上 --reruns 这个参数

1 @pytest.mark.flaky(reruns=6, reruns_delay=2)n2 def test_example(self):n3 print(3)