Pytest是一个非常成熟的全功能的Python测试框架。
新建一个文件require-install.txt,把以下内容放进去:
pytest
pytest-html
pytest-xdist
pytest-rerunfailures
然后在Dos窗口中使用此命令安装所有的模块:
pip install -r require-install.txt
@pytest.mark.skipif(2 > 1, reason="当条件为True时跳过")
注意:setUpClass和tearDownClass需要用@classmethod装饰器装饰。
import unittest
import pytest
class TestCase(unittest.TestCase):
def setUp(self) -> None:
print("unittest 每个用例前置")
def tearDown(self) -> None:
print("unittest 每个用例后置")
@classmethod
def setUpClass(cls) -> None:
print("unittest 所有用例的前置,所有用例之前只执行一次")
@classmethod
def tearDownClass(cls) -> None:
print("unittest 所有用例的后置,所有用例之后只执行一次")
def test_case1(self):
print("测试用例case1")
def test_case2(self):
print("测试用例case2")
if __name__ == '__main__':
pytest.main(["-vs", "pytest_unittest_demo.py"])
(1)setup:在测试函数或类之前执行,完成准备工作,例如数据库链接、测试数据、打开文件等。
(2)teardown:在测试函数或类之后执行,完成收尾工作,例如断开数据库链接、回收内存资源等。
注意:setup、teardown、setup_class、teardown_class都是小写!
class TestCase:
def setup_class(self):
print("pytest 所有测试用例的前置,所有用例执行之前只执行一次!")
def teardown_class(self):
print("pytest 所有测试用例的后置,所有用例执行之后只执行一次!")
def setup(self):
print("pytest 在每一个测试用例执行之前执行一次!")
def teardown(self):
print("pytest 在每一个测试用例执行之后执行一次!")
def test_case3(self):
print("测试用例三case3")
def test_case4(self):
print("测试用例case4")
if __name__ == '__main__':
pytest.main(["-vs", "pytest_demo.py"])
pytest.ini文件是pytest的主配置文件,可以改变pytest的默认行为。
一般放在项目工程的根目录(即当前项目的顶级文件夹下)。
指定pytest的运行方式。
(在cmd输入pytest后,会读取pytest.ini中的配置信息,按指定的方式去运行)
可以用来操作后缀为 .ini 的配置文件;
python标准库(就是python自带的意思,无需安装);
详情请查看另外一篇文章:https://blog.csdn.net/weixin_40608713/article/details/120930460?spm=1001.2014.3001.5501
pytest.main(args,plugins)
参数说明:
1、args :传一个list对象,list 里面是多个命令行的参数。
【包括运行的测试用例(例如:test_a.py);运行测试用例的命令行参数(例如:-vs)】
举例:pytest.main(["-vs", "test_demo.py"])
2、plugins :传一个list对象,list 里面是初始化的时候需注册的插件,可传多个。
举例:
pytest.main(["-vs", "test_demo.py"], plugins=[MyPlugins()]) # plugins指定加载自定义插件MyPlugins()
fixtrue修饰器标记的方法通常用于在其他函数、模块、类或者整个工程调用时会优先执行,通常会被用于完成预置处理和重复操作。例如:登录,执行SQL等操作。
fixture的scope参数:
scope参数:标记方法的作用域。
有4个可选值:function(默认,函数)、class(类)、module(模块)、package/session(包),默认为function。
注意: autouse=True,表示该模块中所有的函数在执行之前都会执行login函数。
# 文件名:pytest-demo.py
import pytest
@pytest.fixture(scope='function',autouse=True)
# autouse=True,表示该模块中所有的函数在执行之前都会执行login函数
def login():
print('登录系统')
def test_01():
print('测试用例一')
class TestCase:
def test_03(self):
print('测试用例三')
def test04(self):
print('测试用例四')
if __name__ == '__main__':
pytest.main(['-s','pytest-demo.py'])
注意:如果没传autouse=True,那么就是默认不自动执行login函数,只有在加了修饰器@pytest.mark.usefixtures(“login”),才会执行login函数。
# 文件名:test_scope_function.py
@pytest.fixture(scope="function") # 如果没传autouse=True,那么就是默认不执行login函数,只有在加了修饰器@pytest.mark.usefixtures("login"),才会执行login函数
def login():
print("登录系统")
def test01():
print("测试用例一")
@pytest.mark.usefixtures("login")
class TestCase:
def test02(self):
print("测试用例二")
def test03(self):
print("测试用例三")
if __name__ == "__main__":
pytest.main(["-s", "test_scope_function.py"])
# 文件名:test_scope_function.py
@pytest.fixture() # 用法三,不传参数,在需要调用login函数的地方以参数的形式传入了执行的方法login
def login():
print("登录系统")
return "success"
def test01(login): # 用法三:以参数的形式传入了执行的方法login
print("测试用例一")
print(login)
class TestCase:
def test02(self, login): # 用法三:以参数的形式传入了执行的方法login
print("测试用例二")
print(login)
def test03(self):
print("测试用例三")
if __name__ == "__main__":
pytest.main(["-s", "test_scope_function.py"])
# 文件名:test_scope_class.py
import pytest
@pytest.fixture(scope="class", autouse=True) # 在TestCase1类和TestCase2类中,login方法都只会被执行一次。函数test_01也会执行一次。
def login():
print("登录系统")
def test01():
print("测试用例一")
class TestCase01:
def test02(self):
print("测试用例二")
def test03(self):
print("测试用例三")
class TestCase02:
def test04(self):
print("测试用例四")
def test05(self):
print("测试用例五")
if __name__ == "__main__":
pytest.main(["-s", "test_scope_class.py"])
# 文件名:test_scope_class.py
@pytest.fixture() # 方法二,不传参数
def login():
print("登录系统")
def test01():
print("测试用例一")
@pytest.mark.usefixtures("login")
# 方法二:此方式函数不会被执行,但是被作用的类中的每个函数执行之前都会执行一次,在测试类前面添加:@pytest.mark.usefixtures('login') 其中login为函数名。
class TestCase01:
def test02(self):
print("测试用例二")
def test03(self):
print("测试用例三")
class TestCase02:
def test04(self):
print("测试用例四")
def test05(self):
print("测试用例五")
if __name__ == "__main__":
pytest.main(["-s", "test_scope_class.py"])
文件名:test_scope_module.py
import pytest
@pytest.fixture(scope="module", autouse=True) # 作用域为module
def login():
print("登录系统")
def test01():
print("测试用例一")
class TestCase:
def test02(self):
print("测试用例二")
def test03(self):
print("测试用例三")
if __name__ == "__main__":
pytest.main(["-s", "test_scope_module.py"])
# 文件名:test_scope_package.py
import pytest
@pytest.fixture(scope="package", autouse=True) # 作用域为包
def login():
print("登录系统")
def test01():
print("测试用例一")
class TestCase1:
def test02(self):
print("测试用例二")
def test03(self):
print("测试用例三")
class TestCase2:
def test04(self):
print("测试用例二")
def test05(self):
print("测试用例三")
if __name__ == "__main__":
pytest.main(["-s", "test_scope_package.py"])
@pytest.fixture() # 不传参数,在需要调用login函数的地方以参数的形式传入了执行的方法login
def login():
print("登录系统")
return "success"
def test01(login): # 以参数的形式传入了执行的方法login
print("测试用例一")
print(login)
class TestCase:
def test02(self, login): # 以参数的形式传入了执行的方法login
print("测试用例二")
print(login)
def test03(self):
print("测试用例三")
if __name__ == "__main__":
pytest.main(["-s", "test_scope_function.py"])
运行结果:
request :是 pytest 的内置 fixture , “为请求对象提供对请求测试上下文的访问权,并且在fixture被间接参数化的情况下具有可选的“param”属性。”
request.param: 获取测试的请求参数。
# 文件名:test_request_param.py
import pytest
# Fixture参数之params参数可实现参数化:(可以为list和tuple,或者字典列表,字典元祖等)
"""
request 是 pytest 的内置 fixture , "为请求对象提供对请求测试上下文的访问权,并且在fixture被间接参数化的情况下具有可选的“param”属性。"
"""
# 测试数据
test_data = ["user1", "user2"]
@pytest.fixture(params=test_data)
def register_users(request): # 特别注意:这里的request参数名是固定的,然后request.param的param没有s哦
# 获取当前的测试数据
user = request.param # request.param 用于获取测试的请求参数
print("拿着这个账号%s 去注册" % user)
result = "success"
return user, result
# test_register()方法被执行2次,此结果类似于ddt数据驱动的功能
def test_register(register_users):
# print("register_users 执行后返回的结果为 %s " % register_users)
user, result = register_users
print("在测试用例里面获取到的测试数据user为 %s" % user)
print(result)
assert result == "success"
if __name__ == '__main__':
pytest.main(["-vs", "test_request_param.py"])
parametrize(argnames,argvalues,indirect=False,ids=None,scope=None)
常用参数:
①argnames:参数名
②argvalues:参数值(可以为list和tuple,或者字典列表,字典元祖等),参数值有N个,用例就会执行N次。
方法一:
# 方法一:
lis = [("admin01", "abc123"), ("admin02", "a123456")] # 元组列表
@pytest.mark.parametrize("username,password", lis)
def test_p(username, password):
print(username, password)
if __name__ == "__main__":
pytest.main(["-vs", "test_pytest_mark_parametrize.py"])
# 方法二:
test_data = [{"user01": "123456"}, {"user02": "654321"}] # 字典列表
@pytest.mark.parametrize("testdata", test_data)
def test_p1(testdata):
print(testdata)
if __name__ == "__main__":
pytest.main(["-vs", "test_pytest_mark_parametrize.py"])
# @File : conftest.py
import pytest
# 将自定义参数添加到pytest配置对象中去(钩子hook函数)
def pytest_addoption(parser):
parser.addoption(
"--env", action="store", default="dev", help="env:表示测试环境,默认dev环境"
)
# 从配置对象中读取自定义参数,方法一:
@pytest.fixture()
def env(request):
return request.config.getoption("--env")
# 从配置对象中读取自定义参数,方法二:
# @pytest.fixture()
# def env(pytestconfig):
# return pytestconfig.getoption("env")
# @File : test_option.py
# 在测试用例中想要获得参数--env的值,就可以调用env函数
from fixture_demo.pytest_addoption.conftest import *
def test_option(env):
if env == 'dev':
print("当前测试环境为:{},域名切换为开发环境".format(env))
elif env == 'test':
print("当前测试环境为:{},域名切换为测试环境".format(env))
else:
print("环境错误,当前环境: {}不存在\n".format(env))
if __name__ == '__main__':
pytest.main(['-s', '--env=test', 'test_option.py'])