pytest是python语言中一款强大的单元测试框架,用来管理和组织测试用例,可应用在单元测试、自动化测试工作中。
unittest也是python语言中一款单元测试框架,但是功能有限,没有pytest灵活。
pytest和unittes就是好用和更好用的对比。所以准备学习unittest的伙伴可以放弃入坑了哈哈哈,纯属调侃pytest是基于unittest封装的,学好unittest就会更好的理解pytest。
1、pytest的安装,怎么装
2、pytest的特征、与unittest的区别
3、pytest如何自动识别用例
4、pytest框架中,用例的运行顺序
5、pytest的简单示例,怎么用
一.pytest 怎么安装
pip install pytest
二.pytest的特征、与unittest的区别
pytest的特征如下:
◆ 自动识别测试用例(unittest当中,需要引入TestSuite,主动加载测试用例。)
◆ 简单的断言表达:assert 表达式即可(unittest当中,self.assert*)
◆ 有测试会话、测试模块、测试类、测试函数级别的fixture(unittest当中是测试类、测试函数级别的fixture)
◆ 有非常丰富的插件,目前在600+,比如allure插件(unittest无)
◆ 测试用例不需要封装在测试类当中(unittest中需要自定义类并继承TestCase)
-
case的py文件名必须是test开头 def用例必须是test开头 class名必须是Test开头,注意大写 class中的def用例必须是test开头
那么pytest是如何自动识别测试用例的呢?我们在编写pytest用例的时候,需要遵守哪些规则呢?
三.pytest如何自动识别用例
识别规则如下:
一.搜索根目录:默认从当前目录中搜集测试用例,即在哪个目录下运行pytest命令,则从哪个目录当中搜索;
二.搜索规则:
1.搜索文件:符合命名规则 test_*.py 或者 *_test.py 的文件
2.在满足1)的文件中识别用例的规则:
以test_开头的函数名;
以Test开头的测试类(没有init函数)当中,以test_开头的函数
四.pytest框架中,用例的运行顺序
原则:先搜索到的py文件中的用例,先执行。在同一py文件当中,按照代码顺序,先搜索到的用例先执行。
五.pytest的简单示例,怎么用
在pytest中,它会首先寻找以test_开头或者以_test结尾的测试模块,然后执行模块里面
以test_开头或者是以_test结尾的测试代码,这里依据这个要去,编写测试模块,如下:
import random
def test_demo1():
"""
pytest 简单示例1
"""
assert 5 ==random.randint(1,10)
运行结果:
其实这就已经看出了pytest和unitest的区别:
1.自动识别测试用例(unittest当中,需要引入TestSuite,主动加载测试用例。)
2.更加简单的断言表达:assert 表达式即可(unittest当中,self.assert*)
3.测试用例不需要封装在测试类当中(unittest中需要自定义类并继承TestCase)
pytest 简单示例2:pytest -v 可以显示详细的运行状况
import random
def test_demo1():
"""
pytest 简单示例1
"""
assert 5 == random.randint(1,10)
def test_demo2():
"""
pytest 简单示例1
"""
assert 1 == 1
def test_demo3():
"""
pytest 简单示例1
"""
assert 3 == 2
命令行运行结果:
pytest提供了很多运行参数,比较常用的有
-k:只执行指定的用例
-s:命令行显示测试代码的输出,如果需要输出html结果最好不要-s
-v:显示详细信息
-q:不显示详细信息
--html=path:输出测试结果到html
--lf:显示错误用例详细信息
这里举两个例子
1.这里如果要指定运行某个模块下某个py文件,在pytest -v 后追加模块名加py文件名
eg:pytest -v jirkou/test_demo.py
2.如果要看错误用例pytest --lf
拿首页v8接口来举例子,让大家看到unitets和pytest的区别
1.case部分
unitest
#test_index_v8.py
import unittest
import requests
class Index_V8(unittest.TestCase):
'''
首页index_v8接口
'''
host = 'https://backend.igengmei.com'
uri = '/api/index/v8'
url = host + uri
common_params = {
"version": "7.24.0",
"device_id": 1234,
"current_city_id": "beijing"
}
def test_index_v8(self):
'''
:return: user self.assert to check
'''
self._testMethodDoc = '获取首页index_v8接口'
res = requests.get(url=self.url, params=self.common_params)
data = res.json()
self.assertEqual(data['error'], 0,'error校验错误!' )
if __name__ == '__main__':
unittest.main()
2.main部分
from unittest import defaultTestLoader
from BeautifulReport import BeautifulReport
# import pickle
if __name__ == '__main__':
suites = defaultTestLoader.discover('testCase') #采集所有suites
result = BeautifulReport(suites) #定义一个result实例
result.report('API自动化测试', 'gmapi_report.html', 'testReport') #跑
pytest就这一步就搞定,直接执行便可以
import requests
def test_index_v8():
"""
pytest 简单示例1
"""
host = 'https://backend.igengmei.com'
uri = '/api/index/v8'
url = host + uri
common_params = {
"version": "7.24.0",
"device_id": 1234,
"current_city_id": "beijing"
}
testMethodDoc = '获取首页index_v8接口'
res = requests.get(url=url, params=common_params)
data = res.json()
assert(data['error'], 1,'error校验,,,,,错误!' )
2.在pytest的测试框架中,测试固件有各种形式的表现。
讲两大常用模块:初始化与清理与强大的参数化的部分
1.初始化与清理
setup_function、teardown_function
setup_module、teardown_module setup
teardown setup_class、teardown_class
setup_method、teardown_method
@pytest.fixture()
这里初始化与清理的方法有些class用例集可用有些def用例可用,并且要区分怎么使用。
class用例集类来说:
setup_function、teardown_function 不能用
setup_module、teardown_module 放在类外可以使用、放在类内不能使用,只在最前和最后调用
setup、teardown 放在类内可以使用、放在类外不能使用,每个case都会调用
setup_class、teardown_class 放在类内可以使用、放在类外不能使用,每个case都会调用
setup_method、teardown_method 放在类内可以使用、放在类外不能使用,每个case都会调用
@pytest.fixture() 放在类内类外都可以使用
对于def用例来说
setup_function、teardown_function 可以使用,每个case都会调用,无需成对使用
setup_module、teardown_module 可以使用,只在最前和最后调用
setup、teardown 可以使用,每个case都会调用
setup_class、teardown_class 不能用
setup_method、teardown_method 不能用
@pytest.fixture() 可以使用
那只有一个放在类内类外都可以使用,且只有pytest拥有,那就是最后一个@pytest.fixture()
详细讲解@pytest.fixture()
@pytest.fixture()是一个装饰器,用于声明函数是一个fixture
如果测试函数的参数中包含fixture名字,那么pytest会检测到
fixture 可以执行测试任务,也可以返回数据给测试函数
命令:pytest --setup -show py文件名,可以查看顺序
pytest --setup-show jirkou/test_demo1.py
fixture()函数放在哪里合适?
1.放在单独的测试文件里
2.如果希望多个测试文件共享fixture,可以在某个公共目录下新建一个conftest文件,讲fixture放在里面
使用装饰器:pytest.fixture(scope='function',autouse=False)
fixture()函数参数解释说明
fixture里面有个scope参数可以控制fixture的作用范围:session>module>class>function
-function:每一个函数或方法都会调用
-class:每一个类调用一次,一个类中可以有多个方法
-module:每一个.py文件调用一次,该文件内又有多个function和class
-session:是多个文件调用一次,可以跨.py文件调用,每个.py文件就是module
"""
pytest 参数化以及作用范围function
"""
import pytest
@pytest.fixture(scope="function")
def get_username():
print("\n获取用户名:")
a = "yuanyuan"
return a
@pytest.fixture()
def get_pwd():
print("\n获取密码:")
b = "123456"
return b
def test_case1(get_username,get_pwd):
"""
测试数据传测试用例fixture
"""
print("测试账号%s"%get_username)
assert get_username == "yuanyuan"
assert get_pwd == "123456"
def test_case2(get_pwd):
print("测试密码%s"%get_pwd)
assert get_pwd == "123456"
"""
pytest 参数化以及作用范围class
"""
import pytest
@pytest.fixture(scope="class")
def get_username():
print("\n获取用户名:")
a = "yuanyuan"
return a
@pytest.fixture(scope="class")
def get_pwd():
print("\n获取密码:")
b = "123456"
return b
class TestCase():
def test_case1(self,get_username,get_pwd):
"""
测试数据传测试用例fixture
"""
print("测试账号%s"%get_username)
assert get_username == "yuanyuan"
assert get_pwd == "123456"
"""
pytest 参数化以及作用范围module
每一个.py文件调用一次,该文件内又有多个function和class
"""
"""
pytest 参数化以及作用范围
"""
import pytest
@pytest.fixture(scope="module")
def get_username():
print("\n获取用户名:")
a = "yuanyuan"
return a
@pytest.fixture(scope="module")
def get_pwd():
print("\n获取密码:")
b = "123456"
return b
class TestCase():
def test_case1(self,get_username,get_pwd):
"""
测试数据传测试用例fixture
"""
print("测试账号%s"%get_username)
assert get_username == "yuanyuan"
assert get_pwd == "123456"
def test_case2(get_pwd):
"""
测试数据传测试用例fixture
"""
print("测试密码%s"%get_pwd)
assert get_pwd == "123456"
import pytest
import requests
import random
@pytest.fixture( scope='function')
def simple_request():
return 55
def test_request(simple_request):
assert simple_request == 55
@pytest.fixture( scope='function')
def simple_request():
print('开始初始化')
yield
after_test()
def after_test():
print('开始清除')
# autoust=False,添加初始化操作函数名作为参数,就会执行初始化操作,不加则不执行 scope='function'
def test_request(simple_request):
print('测试用例1,开始执行测试')
def test_request2(simple_request):
print('测试用例2,开始执行测试')
assert 1 == 1
def test_demo1():
"""
pytest.fixture()初始化简单示例1
"""
print('测试用例3,开始执行测试')
assert 5 == random.randint(4,5)
if __name__ == '__main__':
pytest.main(["-s"]) #-s 为了能看见print的输出效果
运行结果:
collecting ... collected 3 items
test_demo1.py::test_request 开始初始化
PASSED [ 33%]测试用例1,开始执行测试
开始清除
test_demo1.py::test_request2 开始初始化
PASSED [ 66%]测试用例2,开始执行测试
开始清除
test_demo1.py::test_demo1 FAILED [100%]测试用例3,开始执行测试
2.强大的参数化的部分
1.@pytest.fixture,在fixture级别的function参数化
fixture是在测试函数运行前后,由pytest执行的外壳函数。首先来看fixture的函数返回值,也就是返回数值。
先看如下的案例代码:
import pytest
import requests
import random
@pytest.fixture( scope='function')
def simple_list():
return [1,44,5,3]
def test_demo(simple_list):
"""
参数化的简单示例
"""
assert simple_list[1] == 44
import pytest
import requests
import logging
par_to_test=[{
"case":"search a words:pytest",
"headers":{},
"querystring":{
"version": "7.24.0",
"device_id": "7A76480B-0192-4787-AB1A-93F7B9E606FF",
"current_city_id": "beijing"
},
"payload":{},
"status_code":200
}]
@pytest.fixture(params=par_to_test)
def case_deta(request):
print(request.param)
return request.param
def test_case_search(case_deta):
url = "https://backend.igengmei.com/api/index/v7"
logging.captureWarnings(True)
res = requests.request("GET",url,headers=case_deta["headers"],params=case_deta["querystring"], verify=False)
assert res.status_code == case_deta["status_code"]
还是一样,如果希望多个测试文件共享fixture,可以在某个公共目录下新建一个conftest文件,将fixture放部分在里面
2.@pytest.mark.parametrize() 允许在function或class级别的参数化,为特点的测试函数或类提供类多个argument/fixture设置
import pytest
def add(x,y):
return x+y
@pytest.mark.parametrize(
"x,y,expected",
[
(1,1,2),
(2,2,4),
(10,10,20),
]
)
def test_add(x,y,expected):
assert add(x,y) == expected