Pytest的基本使用

it2024-01-20  59

目录

一: Pytest的安装:二: pytest的简单案例:三: 断言1: assert断言:2:异常断言测试:抛出的异常和指定的一致测试: 抛出的异常和测试的异常不一致。测试:将异常信息存储到变量中。测试:通过异常的内容捕获异常内容。 3:警告断言测试警告断言:将警告信息写入变量:通过警告内容捕获信息 四:setup和teardown函数案例:验证函数级别案例:验证类级别案例: 验证模块级别: 五:pytest.fixture的使用1: fixture的简单使用:2:conftest.py文件的使用3:@pytest.mark.usefixtures装饰 六:fixture参数1: scope参数:指定被标记函数(方法)的作用域测试function:测试:class测试: module: 2: params参数3:autouse参数 七:mark标记1: pytest.mark.xfail()2:pytest.mark.skip()3:pytest.mark.skipif()4: pytest.mark.parametrize() 八:配置文件九:常用插件1: 插件的安装2:生成测试报告3: 控制测试用例的执行顺序4: 失败重试5:取消插件 十: Yaml1:Yaml的语法规则2:Yaml数据结构的使用3: Python读取Yaml文件和写入Yaml文件1: 安装PyYaml库:2:读取Yaml文件:3:将内容写入到Yaml文件

一: Pytest的安装:

pip install -U pytest -i https://pypi.tuna.tsinghua.edu.cn/simple

二: pytest的简单案例:

Pytest能够识别test开头的方法和Test开头的类。默认不打印,需要指定打印: pytest.main([’-s’])如果类不以Test开头,类中的方法即使是以test开头也不会执行。 import pytest def test_01(): print("我是test_01") def test_02(): print("我是test_02") class Test_demo(object): def test_03(self): print("我是test_03") def test_04(self): print("我是test_04") class Tsts_demo(object): def test_05(self): print("我是test_05") if __name__ == '__main__': pytest.main(['-s'])

运行结果:

三: 断言

断言是python自带的,不是unitest中的。断言的分类: assert断言,异常断言, 警告断言。

1: assert断言:

当断言预测失败时,备注信息会以AssertionError抛出,并在控制台输出。 案例: import pytest def test_1(): num = 1 + 1 assert num == 2, 'num==2条件不满足' def test_2(): num = 1 + 1 assert num == 3, 'num==3条件不满足' if __name__ == '__main__': pytest.main(['-s'])

运行结果:

2:异常断言

出现指定异常则通过,如果出现不是指定的异常则报错。pytest.raises(预期的异常类,match = 异常的匹配信息)with pytest.raises(TypeError) as 变量: 这个变量存储该异常的所有信息。变量.__dict__获取变量的所有属性。

案例: 先给定一个闰年的函数作为被测试的函数:

# 判断是否是闰年的函数 def is_leap_year(year): # 先判断year是不是整型 if isinstance(year, int) is not True: raise TypeError("传入的参数不是整数") elif year == 0: raise ValueError("公元元年是从公元一年开始!!") elif abs(year) != year: raise ValueError("传入的参数不是正整数") elif (year % 4 == 0 and year % 100 != 0) or year % 400 == 0: print("%d年是闰年" % year) return True else: print("%d年不是闰年" % year) return False

测试:抛出的异常和指定的一致

class TestAssert(object): def test_01(self): with pytest.raises(TypeError): # 传入的是字符串,将会抛出TypeError的异常,但是pytest.raises指定的也是TypeError所以测试通过。 is_leap_year('2020')

测试: 抛出的异常和测试的异常不一致。

class TestAssert(object): def test_01(self): with pytest.raises(TypeError): # 传入的是0,将会抛出ValueError的异常,但是pytest.raises指定的是TypeError所以测试不通过,抛出异常。 is_leap_year(0)

测试:将异常信息存储到变量中。

class TestAssert(object): def test_01(self): with pytest.raises(TypeError) as err_info: # 传入的是0,将会抛出ValueError的异常,但是pytest.raises指定的是TypeError所以测试不通过,抛出异常。 is_leap_year('2000') print("出现的异常类型是:", err_info.type) print("异常的所有信息:", err_info.__dict__)

测试:通过异常的内容捕获异常内容。

匹配异常信息中必须有参数这两个字,并且类型是TypeError。

class TestAssert(object): def test_01(self): with pytest.raises(TypeError, match="参数") as err_info: # 传入的是0,将会抛出ValueError的异常,但是pytest.raises指定的是TypeError所以测试不通过,抛出异常。 is_leap_year('2000')

3:警告断言

警告断言最大的区别是变量可以存储多个警告。pytest.warns()

主备工作:自定义三个抛出警告函数:

def make_warn(): # 抛出版本更新警告 warnings.warn("deprecated", DeprecationWarning) def not_warn(): # 不抛出任何警告 pass def user_warn(): # 抛出兼容性警告 warnings.warn("user warn", UserWarning)

测试警告断言:

class TestWarns(object): # 测试异常和自己定义的一致 def test_01(self): with pytest.warns(DeprecationWarning): make_warn() # 测试异常和自己的不一致 def test_02(self): with pytest.warns(DeprecationWarning): user_warn() # 测试没有异常 def test_03(self): with pytest.warns(DeprecationWarning): not_warn()

测试结果:

将警告信息写入变量:

class TestWarns(object): # 测试异常和自己定义的一致 def test_01(self): with pytest.warns((DeprecationWarning, UserWarning )) as record: make_warn() user_warn() print("存储异常信息变量的长度:", len(record)) print("存储异常信息变量第一个的信息:", record[0].message) print("存储异常信息变量第一个的异常类型:", record[0].category) print("异常信息的所有属性", record.__dict__)

测试结果:

通过警告内容捕获信息

class TestWarns(object): # 测试异常和自己定义的一致 def test_01(self): with pytest.warns((DeprecationWarning), "user") as record: make_warn()

结果:因为匹配内容中没有user,所以报错。

四:setup和teardown函数

函数级别:setup_function() teardown_function()

作用范围:只针对类外面的test函数起作用,每个都执行一次。

类级别: setup_class() teardown_class() setup_method() teardown_method()

setup_class() teardown_class(): 针对当前类只执行一次。 setup_method() teardown_method(): 针对当前类中的方法,每次执行都执行一遍。

模块级别:setup_model() teardown_model()

只有执行当前文件的开始和结束执行一次。

案例:验证函数级别

import pytest def test_01(): print("我是普通函数01") def test_02(): print("我是普通函数02") def setup_function(): print("我是普通函数的前置函数") def teardown_function(): print("我是普通函数的后置函数")

案例:验证类级别

import pytest class Test_Demo(object): def test_01(self): print("我是类中函数01") def test_02(self): print("我是类中函数02") @classmethod def setup_class(self): print("我是类前置函数") @classmethod def teardown_class(self): print("我是类后置函数") def setup_method(self): print("我是类中方法前置函数") def teardown_method(self): print("我是类中方法后置函数") if __name__ == '__main__': pytest.main()

案例: 验证模块级别:

import pytest def setup_module(): print("我是模块前置函数") def test_01(): print("我是测试函数") def teardown_module(): print("我是模块后置函数") if __name__ == '__main__': pytest.main()

五:pytest.fixture的使用

1: fixture的简单使用:

装饰在普通的函数上面,测试函数中传入普通函数的名,测试函数内部可以用普通函数的名,代表普通函数的返回值。

案例: 注意:运行测试函数前先运行被fixture装饰的函数。

import pytest @pytest.fixture() def get_url(): print("我就是个普通函数") return "http://www.baidu.com" def test_web(get_url): print("正在运行测试函数") print("打印get_url的返回值:", get_url) if __name__ == '__main__': pytest.main()

2:conftest.py文件的使用

(这个模块里面的被fixture装饰的函数可以直接用)

如果在测试中多个测试文件中用例用到同一个的fixture函数,则可以将其移动到conftest.py文件中, 所需的fixture对象会自动被pytest发现,而不需要再每次导入。

conftest.py文件名固定

在conftest.py文件中实现共用的fixture函数

案例: 1: 新建conftest.py:里面定义一个被@pytest.fixture()修饰只返回百度地址的函数。

import pytest @pytest.fixture() def get_baidu_url(): return "www.baidu.com"

2: 另一个文件不导包的情况下直接用conftest中的函数名。

import pytest def test_01_demo(get_baidu_url): print(get_baidu_url) if __name__ == '__main__': pytest.main()

3: 运行结果:

3:@pytest.mark.usefixtures装饰

作用 : 如果我不想使用被装饰函数的返回值,但是需要函数执行前调用一下被装饰函数,那么可以使用@pytest.mark.usefixtures('被装饰函数名')来处理。 案例:

import pytest @pytest.fixture() def return_url(): print("我是百度地址: www.baidu.com") return "www.baidu.com" @pytest.mark.usefixtures('return_url') def test_demo(): print("我是测试函数") if __name__ == '__main__': pytest.main()

六:fixture参数

1: scope参数:指定被标记函数(方法)的作用域

function : 每个测试用例都要执行一次。(默认)class: 作用于整个类,每个类只执行一次。module : 作用域整个模块(文件),每个模块只执行一次。session : 作用域整个项目,每个项目只执行一次。

测试function:

import pytest @pytest.fixture(scope='function') def foo(): print("我是被fixture修饰的函数") return "哈喽哈喽......." def test_01(foo): print("我是普通测试01:") print(foo) def test_02(foo): print("我是普通测试02") print(foo) class Test_demo(object): def test_03(self, foo): print("我是类中的测试用例03: ") print(foo) def test_04(self, foo): print("我是类中的测试用例04: ") print(foo) if __name__ == '__main__': pytest.main()

测试:class

修改刚才代码: 变成class,运行结果:注意:非类的测试用例还是每次测试用例都要调用。

测试: module:

2: params参数

params 参数接收list类型的参数。对于param里面的每个值,fixture函数都会去遍历执行一次相应的每次都会驱动使用fixture函数的测试函数执行一次。 测试 import pytest @pytest.fixture(params=['renshanwen', '15', '1173714248@qq.com']) def username(request): print("我是被fixture修饰的函数") return request.param def test_demo(username): print("我是测试用例") print(username) if __name__ == '__main__': pytest.main()

运行结果:

由于装饰器有列表中有三个,所以测试用例也会执行三次,就会调用三次被装饰函数。

3:autouse参数

pytest.fixture(autouse=False) 的autouse参数默认为False, 不会自动执行。设置为True时,当前运行的所有测试函数在运行前都会执行fixture函数。

演示:

import pytest @pytest.fixture(autouse=True) def before(): print('不管你是谁,都要先调用我') def test_1(): print("test_1") class Test2: def test_2(self): print('test_2') def test_3(self): print('test_3')

七:mark标记

1: pytest.mark.xfail()

1: 将测试函数标记为预期失败。

import pytest @pytest.mark.xfail(reason = "功能不完善") def test_1_demo(): print("我就是个被装饰的") if __name__ == '__main__': pytest.main()

运行结果:

2:pytest.mark.skip()

1: 无条件跳过测试用例:

import pytest @pytest.mark.skip(reason = "跳过执行") def test_1_demo(): print("我就是个被装饰的") if __name__ == '__main__': pytest.main()

3:pytest.mark.skipif()

1:有条件地跳过测试函数。

import pytest @pytest.mark.skipif(condition= True, reason = "跳过") def test_1_demo(): print("我就是个被装饰的111111111111111") @pytest.mark.skipif(condition= False, reason = "不跳过") def test_2_demo(): print("我就是个被装饰的222222222222222") if __name__ == '__main__': pytest.main()

2: 运行结果:

4: pytest.mark.parametrize()

目的:参数化Fixture方法和测试函数

import pytest @pytest.mark.parametrize(['username', 'password'], [('renshanwen', '12345678'), ('niuniu', '87654321')]) def test_login(username, password): print("\n") print("username是:", username) print("password是:", password) if __name__ == '__main__': pytest.main()

运行结果:

八:配置文件

配置文件名字, pytest.ini固定。配置文件中的注释是 ;addopts = -s test_20.py : 指定运行的命令testpaths = ./ : 指定测试文件的路径python_files = test*.py : 识别以test开头的文件python_classes = Test* : 识别以Test开头的类python_functions = mike* : 识别以test开头的函数 案例: [pytest] addopts = -s test_12.py test_13.py testpaths = ./ python_files = test_*.py python_classes = Test_* python_functions = test_* ;在ini文件中注释语句是以分号开始的, 所有的注释语句不管多长都是独占一行直到结束的

九:常用插件

1: 插件的安装

workon test_v7 # 进入虚拟环境 pip install pytest-html # 生成测试报告 pip install pytest-ordering # 控制函数执行顺序 pip install pytest-rerunfailures # 失败重试

2:生成测试报告

1: 测试代码:

import pytest def test_01(): print("测试成功的测试用例") assert True def test_02(): print("测试失败的测试用例") assert False if __name__ == '__main__': pytest.main()

2: 执行命令:

pytest -s test17_port.py --html=./report.html

3: 报告展示:

3: 控制测试用例的执行顺序

import pytest @pytest.mark.run(order= 3) def test_01(): print("我是测试用例一") @pytest.mark.run(order=1) def test_02(): print("我是测试用例二") @pytest.mark.run(order=2) def test_03(): print("我是测试用例三") if __name__ == '__main__': pytest.main()

运行结果:

4: 失败重试

1: 代码:

import pytest def test_01(): print("测试成功的测试用例") assert True def test_02(): print("测试失败的测试用例") assert False if __name__ == '__main__': pytest.main()

2:执行命令:

pytest -s test17_port.py --reruns 2

3: 运行结果:

5:取消插件

-p no:ordering : 取消排序-p no:html : 取消生成测试报告-p no:rerunfailures : 取消失败重试

十: Yaml

1:Yaml的语法规则

大小写敏感。

使用缩进表示层级关系。

缩进时不允许使用tab键,只允许使用空格。

缩进的空格数目不重要,只要相同层级的元素左侧对齐即可。

Yaml在线编辑器地址: http://old.qqe2.com/jsontool/yaml.php

2:Yaml数据结构的使用

1: Yaml与python的转换: 注意:Yaml对象冒号后面的空格不能省略。 2: Yaml中的数组: 3: Yaml中的锚点和引用:

3: Python读取Yaml文件和写入Yaml文件

1: 安装PyYaml库:

pip3 install -U PyYAML

2:读取Yaml文件:

准备测试文件test.yaml

Search_Data: search_test_001: value: 456 expect: [4,5,6] search_test_002: value: "你好" expect: {"value":"你好"}

yaml.read.py文件中读取test.yaml中的数据:

import yaml with open("./test.yaml", "r") as f: data = yaml.load(f, Loader=yaml.FullLoader) print(data

运行结果:

3:将内容写入到Yaml文件

1: yaml_write.py进行写入

import yaml data={'Search_Data': { 'search_test_002': {'expect': {'value': '你好'}, 'value': '你好'}, 'search_test_001': {'expect': [4, 5, 6], 'value': 456} } } # 要设置编码格式,否则会出现中文乱码 with open('./yaml_hello.yaml', 'w', encoding='utf-8') as f: yaml.dump(data, f,allow_unicode=True)

2: 执行结果:

最新回复(0)