- pytest
- unittest
1、pytest
默认规则
- 模块名必须为test_开头或者_test结尾
- 类名必须为Test开头,并且不能有__init__方法
- 函数名和方法名必须为test开头或者结尾
- 执行顺序为自上而下
1.1 安装
# requirement.txt
pytest
pytest-html # html报告插件
pytest-xdist # 多并发执行插件
pytest-ordering # 排序执行插件
pytest-rerunfailures # 重测插件
allure-pytest # allure报告插件
# 安装
pip install -r requirement.txt
1.2 使用
语法:pytest [options] [filename/filepath/nodeid]
1.2.1 可选参数
-s:输出调试信息及打印信息
-v:输出每个案例详细信息及测试结果
-x:报错停止,后面的案例都不会运行
-n=int:多线程运行,int表示开的线程数
-k=str:筛选运行含有指定字符串的案例
-m=str:只执行被分组标记过的,str表示组名,可以使用and or not连接分组
–reruns=int:失败重跑,int表示重跑的次数
–maxfail=int:设置允许报错次数,int表示允许报错次数
–html=path:自动生成html报告,path表示生成报告的路径
–allure=path:生成allure报告的临时数据,path表示生成路径
1.2.2 命令行
# nodeid:由模块名,分隔符,文件名,类名, 方法名, 函数名组成
# test_mod.py::TestClass::test_method, 分割符为::
示例:
pytest -vs ./testcase # 指定路径
pytest -s test_mod.py # 指定文件
pytest -v test_mod.py::TestClass::test_method # 指定nodeid
1.2.3 main函数
# 传参列表,每个元素都是pytest的运行参数
示例:
if __name__ == '__main__':
pytest.main(['-vs', '-n=4'])
1.2.4 ini配置文件
1.在项目根目录创建pytest.ini(必须为这个名字)
2.添加以下信息
# pytest.ini
[pytest]
addopts=-vs -n=2
testpaths=./testcase
python_files=test_*.py
python_classes=Test*
python_functions=test
markers=
test01:组名1
test02:组名2
# 含义
[pytest] :标识声明标题,固定写法,必须写在第一行
addopts:命令参数选项,多个参数用空格隔开
testpaths:搜索路径
python_files:搜索文件规则
python_classes:搜索类规则
python_functions:搜索函数和方法规则
markers:分组配合信息
# 运行
项目根目录下,cmd内输入pytest即可
1.2.5 断言
使用assert关键字进行断言
1.3 内置装饰器
1.3.1 标记排序
@pytest.mark.run(order=1)
在函数或方法上加以上装饰器,即可排序执行
执行顺序:0和正整数 > 没标记的 > 负整数
在各个阶段,数字越小运行级别越高
order:执行顺序
示例:
@pytest.mark.run(order=1)
def test_1():
print('test_1...')
@pytest.mark.run(order=2)
def test_2():
print('test_2...')
@pytest.mark.run(order=-1)
def test_3():
print('test_3...')
@pytest.mark.run(order=-2)
def test_4():
print('test_4...')
def test_5():
print('test_5...')
1.3.2 标记分组
@pytest.mark.groupname
标记分组,可通过配置执行分组的所有案例
groupname:组名,自定义
示例:
@pytest.mark.mm
def test_m1():
print('test_m1...')
@pytest.mark.mm
def test_m2():
print('test_m2...')
1.3.3 标记跳过
无条件跳过
@pytest.mark.skip(reason=None)
跳过指定函数或方法
reason:过滤原因,可选有条件跳过
@pytest.mark.skipif(condition, reason=None)
满足条件即跳过指定函数或方法
condition:条件,bool或字符串形式的代码,成立标记,反之不标记,必选
reason:原因,可选
示例:
@pytest.mark.skip(reason='xxx') # 无条件跳过
def test_sk1():
print('test_sk1...')
@pytest.mark.skipif(condition=True, reason='xxx') # 条件成立跳过
def test_sk2():
print('test_sk2...')
1.3.4 标记失败
@pytest.mark.xfail(reason=‘test’, raises=None, condition=None)
标记预期会出现异常的测试,异常结果显示xfail,未异常显示xpass
condition:条件,bool或字符串形式的代码,成立标记,反之不标记,可选
raises:预期抛出的异常类型,符合预期显示xfail,不符合显示xpass,可选
reason:原因,可选
示例:
@pytest.mark.xfail(condition=True, reason='xxx') # 条件成立标记
def test_xf1():
print('test_xf1 ...')
@pytest.mark.xfail(raises=Exception) # 符合预期错误
def test_xf2():
print('test_xf2...')
raise Exception('xxx')
@pytest.mark.xfail(reason='xxx') # 标记原因
def test_xf3():
print('test_xf3...')
1.3.5 传参
@pytest.mark.parametrize(argnames, argvalues, ids=None)
从列表或元组内依次取值,传不同值重复执行案例
argnames::参数名称,字符串(使用逗号分割变成多参数)或列表或元组,必选
argvalues:参数值,列表或元组(多参数要使用列表套元组),必选
ids: 测试id, 可选
示例:
@pytest.mark.parametrize(['a'], [(1,), (2,), (3,)]) # 列表元组形式,单参数
def test_d(a):
print(a)
@pytest.mark.parametrize(['a', 'b'], [(1,2), (2,3), (3,4)]) # 列表元组形式,多参数
def test_d(a, b):
print(a, b)
@pytest.mark.parametrize('a', [1,2,3]) # 字符串形式,单参数
def test_e(a):
print(a)
@pytest.mark.parametrize('a,b', [(1,2), (2,3), (3,4)]) # 列表元组形式,多参数
def test_f(a,b):
print(a,b)
1.4 夹具
夹具:测试前的条件和测试后处理
setup:测试之前执行
teardown:测试后执行
1.4.1 模块级别
示例:
def setup_module(args):
print('setup_model...', args)
def teardown_module(args):
print('teardown_model...', args)
1.4.2 函数级别
示例:
def setup_function(args):
print('setup_function...', args)
def teardown_function(args):
print('teardown_function...',args)
1.4.3 类级别
示例:
def setup_class(self):
print('setup_class...')
def teardown_class(self):
print('teardown_class...')
1.4.4 方法级别
示例:
def setup_method(self, args):
print('setup_method...', args)
def teardown_method(self, args):
print('teardown_method...', args)
def setup(self):
print('setup...')
def teardown(self):
print('teardown...')
1.4.5 自定义
使用fixture装饰器自定义夹具
fixture(scope=‘function’, params=None, autouse=False)
scope:夹具级别, session(整个项目),module,function(包含method),class
autouse:是否自动使用
定义夹具
无返回值:
@pytest.fixture()
def custom_fixture1():
print('custom_fixture1...')
有返回值:
@pytest.fixture()
def custom_fixture2():
print('custom_fixture2...')
return 1
有参数:
@pytest.fixture(params=[1,2,3])
def custom_fixture3(request):
print('custom_fixture3...', request.param)
return request.param
使用夹具
方式一:适合无返回值
@pytest.mark.usefixtures('custom_fixture1')
def test_1():
print('test_1...')
方式二:适合有返回值
def test_2(custom_fixture2):
print('test_2...', custom_fixture2)
1.5 报告
1.5.1 配置及使用
1、下载allure源码包并解压(https://github.com/allure-framework/allure2/tags)
2、下载jdk并安装
3、配置allure和jdk的环境变量
4、执行案例并生成临时文件:pytest
5、生成allure报告指令:allure generate ./temp -o ./report --clean
2、unittest
默认规则
- 测试类必须继承于 unittest.TestCase,并且不能有__init__方法
- 函数名和方法名必须为test开头,且不能有参数
2.1 使用
# 写测试类,然后使用main函数运行
示例:
if __name__ == '__main__':
unittest.main()
2.2 断言
使用assert关键字进行断言
使用TestCase的方法断言
TestCase的断言方法
def assertEqual(self, first: Any, second: Any, msg: Any = ...) -> None: ...
def assertNotEqual(self, first: Any, second: Any, msg: Any = ...) -> None: ...
def assertTrue(self, expr: Any, msg: Any = ...) -> None: ...
def assertFalse(self, expr: Any, msg: Any = ...) -> None: ...
def assertIs(self, expr1: Any, expr2: Any, msg: Any = ...) -> None: ...
def assertIsNot(self, expr1: Any, expr2: Any, msg: Any = ...) -> None: ...
def assertIsNone(self, obj: Any, msg: Any = ...) -> None: ...
def assertIsNotNone(self, obj: Any, msg: Any = ...) -> None: ...
def assertIn(self, member: Any, container: Union[Iterable[Any], Container[Any]], msg: Any = ...) -> None: ...
def assertNotIn(self, member: Any, container: Union[Iterable[Any], Container[Any]], msg: Any = ...) -> None: ...
def assertIsInstance(self, obj: Any, cls: Union[type, Tuple[type, ...]], msg: Any = ...) -> None: ...
def assertNotIsInstance(self, obj: Any, cls: Union[type, Tuple[type, ...]], msg: Any = ...) -> None: ...
def assertGreater(self, a: Any, b: Any, msg: Any = ...) -> None: ...
def assertGreaterEqual(self, a: Any, b: Any, msg: Any = ...) -> None: ...
def assertLess(self, a: Any, b: Any, msg: Any = ...) -> None: ...
def assertLessEqual(self, a: Any, b: Any, msg: Any = ...) -> None: ...
...
2.3 子测试
class TestSub(unittest.TestCase):
def test_sub(self):
for i in range(10):
with self.subTest(i): # 子测试,多次执行该测试
print(f"test_sub: {i}...")
2.4 夹具
2.4.1 模块级别
def setUpModule():
print('setUpModule...')
def tearDownModule():
print('tearDownModule...')
2.4.2 类级别
@classmethod
def setUpClass(cls):
print('setUpClass...')
@classmethod
def tearDownClass(cls):
print('tearDownClass...')
2.4.3 方法级别
def setUp(self):
print('setUp...')
def tearDown(self):
print('tearDown...')
2.5 测试套件
TestSuite:用来自己组织待测试的内容和顺序
TextTestRunner:执行测试并将结果写到TestResult中
HtmlTestRunner:执行测试并生成Html报告,安装:pip install html-testRunner
defaultTestLoader:根据规则自动寻找并添加case
1. 创建TestSuite对象
2. 添加测试内容
3. 运行测试
# 方法一
suite = unittest.TestSuite() # 实例化suite对象
suite.addTest(Txt("test_02")) # 添加一条case
suite.addTests(map(Txt, ['test_01', 'test_02'])) # 添加多条case
runner = unittest.TextTestRunner() # 生成runner对象
runner.run(suite) # 运行测试
# 方法二
import HtmlTestRunner
# 根据规则自动寻找并添加case,path:寻找开始路径, pattern:寻找规则,并生成suite对象
suite = unittest.defaultTestLoader.discover(path, pattern='test_*.py')
runner = HtmlTestRunner.runner.HTMLTestRunner() # 生成runner对象
runner.run(suite) # 运行测试