目录





一: 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'])

运行结果:

Pytest的基本使用_python

三: 断言


  • 断言是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'])

运行结果:

Pytest的基本使用_python_02

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')

Pytest的基本使用_测试用例_03

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

class TestAssert(object):

def test_01(self):

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

Pytest的基本使用_异常信息_04

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

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__)

Pytest的基本使用_python_05

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

匹配异常信息中必须有参数这两个字,并且类型是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')

Pytest的基本使用_测试用例_06

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()

测试结果:

Pytest的基本使用_测试用例_07

将警告信息写入变量:

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__)

测试结果:

Pytest的基本使用_异常信息_08

通过警告内容捕获信息

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

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

Pytest的基本使用_python_09

四: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("我是普通函数的后置函数")

Pytest的基本使用_异常信息_10

案例:验证类级别

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()

Pytest的基本使用_python_11

案例: 验证模块级别:

import pytest

def setup_module():
print("我是模块前置函数")

def test_01():
print("我是测试函数")

def teardown_module():
print("我是模块后置函数")


if __name__ == '__main__':
pytest.main()

Pytest的基本使用_异常信息_12

五: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()

Pytest的基本使用_异常信息_13

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: 运行结果:

Pytest的基本使用_异常信息_14

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()

Pytest的基本使用_测试用例_15

六: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()

Pytest的基本使用_python_16

测试:class

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

Pytest的基本使用_python_17

测试: module:

Pytest的基本使用_测试用例_18

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()

运行结果:

Pytest的基本使用_测试用例_19

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

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')

Pytest的基本使用_python_20

七:mark标记

1: pytest.mark.xfail()

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

import pytest

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

if __name__ == '__main__':
pytest.main()

运行结果:

Pytest的基本使用_异常信息_21

2:pytest.mark.skip()

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

import pytest

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

if __name__ == '__main__':
pytest.main()

Pytest的基本使用_python_22

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: 运行结果:

Pytest的基本使用_测试用例_23

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的基本使用_异常信息_24

八:配置文件


  • 配置文件名字, 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: 报告展示:

Pytest的基本使用_异常信息_25

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()

运行结果:

Pytest的基本使用_python_26

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: 运行结果:

Pytest的基本使用_python_27

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对象冒号后面的空格不能省略。

Pytest的基本使用_python_28

2: Yaml中的数组:

Pytest的基本使用_异常信息_29

3: Yaml中的锚点和引用:

Pytest的基本使用_异常信息_30

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

运行结果:

Pytest的基本使用_异常信息_31

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: 执行结果:

Pytest的基本使用_异常信息_32