一、单元测试框架
1、什么是单元测试框架?
单元测试框架是在自动化测试或者白盒测试中对软件的最小单元(函数,方法)进行测试的框架
2、单元测试框架分类
- python:unittest、pytest(主流)
- java:Test(主流)、Junit
3、单元测试框架主要做什么?
- 发现测试用例
- 执行测试用例
- 判断测试结果
- 生成测试报告
二、Pytest
1、Pytest简介
- pytest是一个非常成熟的单元测试框架,灵活、简单
- 它可以结合selenium、requests,appium完成各种不同的自动化
- 它可以生成自定义的allure报告以及和Jenkins持续集成
- pytest有很多强大的插件
pytest
- pytest-html(生成html报告的插件)
- pytest-xdist(多线程运行的插件)
- pytest-ordering(改变用例的执行顺序的插件)
- pytest-rerunfailures(失败用例重跑的插件)
- allure-pytest(生成美观自定义的allure报告)
2、插件安装
通过在项目的根目录下新建一个:requirements.txt文件保存插件,通过以下命令安装:
pip install -r requirements.txt
pip install -r requirements.txt
三、Pytest测试用例的规则及应用
1、规则
- 模块名必须以test_开头或者_test结尾
- 测试类必须以Test开头,并且不能带有init方法
- 测试用例必须以test_开头
2、应用
(Alt+Enter自动导包)
- 通过命令行方式执行
pytest
# -vs:-v表示输出详细信息,-s输出调试信息
pytest -vs
# -n:表示多线程运行(需安装插件pytest-xdist)
pytest -vs -n=2
# --reruns num:失败重跑(需安装插件pytest-rerunfailures)
# raise Exception('抛出异常')
pytest -vs --reruns=2
# -x:出现一个失败则停止测试
pytest -vs -x
# --maxfail:出现两次失败才终止
pytest -vs --maxfail=2
# --html:生成html测试报告(需安装插件pytest-html)
pytest -vs --html
# -k:运行测试用例名称包含包含某个字符串
pytest -vs -k "zhangsan"
pytest -vs -k "zhangsan" or "wangwu"
- 通过主函数main方式执行
import pytest
if __name__ == '__main__':
pytest.main(["-vs"])
- 通过全局配置pytest.ini文件执行
注意:
- 一般放在项目的根目录下,名称必须是pytest.ini
- 当有中文出现,可能需要改变编码格式为GB2312
- pytest.ini文件可以改变默认的测试用例规则
- 不管是命令行运行还是主函数运行,都会加载这个配置文件
[pytest]
# 参数
# 命令
# -m "smoke表示只执行冒烟用例(在对应方法的上方加装饰器)
addopts = -vs -m "smoke"
# 路径
testpaths = ./testcases
# 模块名称
python_files = test_*.py
# 类名
python_classes = Test*
# 测试方法
python_functions = test_*
# 测试用例分组执行
# 标记
markers =
smoke:冒烟用例
mashang:马上教育
mroduct_manage:商品管理模块用例
user_manage:用户管理模块
import time
import pytest
class TestMashang:
@pytest.mark.smoke
def test_zhangsan(self):
print('测试zhangsan')
# raise Exception('我抛出异常啦')
def test_wangwu(self):
print('测试wangwu')
@pytest.mark.smoke
def test_lisi(self):
# time.sleep(3)
print('测试lisi')
四、Pytest跳过测试用例
import time
import pytest
class TestMashang:
workage = 8
def test_wangwu(self):
print('测试wangwu')
@pytest.mark.skip(reason='无理由跳过')
def test_01(self):
print('测试01')
@pytest.mark.skipif(workage<10, reason='工作年限小于10年跳过')
def test_01(self):
print('测试02')
五、Pytest测试用例的前后置
1、前后置
class TestMashang:
def setup_class(self):
print('每个类之前执行一次')
def teardown_class(self):
print('每个类之后执行一次')
def setup_method(self):
print('每个用例之前执行一次')
def teardown_method(self):
print('每个用例之后执行一次')
def test_zhangsan(self):
print('测试zhangsan')
def test_wangwu(self):
print('测试wangwu')
2、使用fixture实现部分前后置
@pytest.fixture(scope.None, autouse=Fasse,params=None, ids=None, name=None)
2.1 scope
scope:作用域
- function:在函数之前和之后执行
- 手动调用的方式是在测试用例的参数中加入fixture的名称
- 如果fixture有通过return或yield返回值的话,那么可以把这个值传递到测试用例中,值是通过固件的名称传递的
- class:在类之前和之后执行
- 手动调用的方式是在类的上面加上@pytest.mark.usefixtures(“exe_database_sql”)
- package/session:在整个项目会话之前和之后执行
- 一般会结合confitest.py文件来实现
- 一般会结合confitest.py文件来实现
2.2 autouse
autouse:自动执行,默认false(无需调用,自动执行)
import time
import pytest
from common.common_util import CommonUtil
@pytest.fixture(scope="function")
def exe_database_sql():
print('执行sql查询')
class TestMashang(CommonUtil):
def test_zhangsan(self):
print('测试zhangsan')
def test_lisi(self,exe_database_sql):
print('测试lisi')
import time
import pytest
from common.common_util import CommonUtil
@pytest.fixture(scope="function",autouse=True)
def exe_database_sql():
# 之前
print('执行sql查询')
yield
# 之后
print('关闭数据库连接')
class TestMashang(CommonUtil):
def test_zhangsan(self):
print('测试zhangsan')
def test_wangwu(self):
print('测试wangwu')
如果需要在另外一个py文件中调用需要结合contest.py文件使用
2.3 params
params:实现参数化
如何把值传到fixture?
通过fixture函数的参数里面加入request来接受参数,然后通过request.param来取值(这里的param没有s)
# 读取数据的方法
def read_yaml():
return ["chenglong", 'zengzidan', 'caiyili']
@pytest.fixture(scope="function", autouse=False, params=read_yaml())
def exe_database_sql(request):
print('执行sql查询')
yield request.param
print('关闭数据库连接')
class TestMashang():
def test_wangwu(self, exe_database_sql):
print('测试wangwu:'+exe_database_sql)
def read_yaml():
# return ["chenglong", 'zengzidan', 'caiyili']
return [{'a': 'b'}]
@pytest.fixture(scope="function", autouse=False, params=read_yaml())
def exe_database_sql(request):
print('执行sql查询')
yield request.param
print('关闭数据库连接')
class TestMashang():
def test_wangwu(self, exe_database_sql):
print('测试wangwu'+str(exe_database_sql))
2.4 ids
ids:不能单独使用,必须params一起使用,作用是对参数起别名
# 读取数据的方法
def read_yaml():
return ["chenglong", 'zengzidan', 'caiyili']
@pytest.fixture(scope="function", autouse=False, params=read_yaml(), ids=['ch', 'z', 'ca'])
def exe_database_sql(request):
print('执行sql查询')
yield request.param
print('关闭数据库连接')
2.5 name
name:作用是给fixture起别名
特别注意:一旦使用了别名,那么fixture的名称就不能再用了,只能用别名
# 读取数据的方法
def read_yaml():
return ["chenglong", 'zengzidan', 'caiyili']
@pytest.fixture(scope="function", autouse=False, params=read_yaml(), ids=['ch', 'z', 'ca'], name='db')
def exe_database_sql(request):
print('执行sql查询')
yield request.param
print('关闭数据库连接')
class TestMashang():
def test_wangwu(self, db):
print('测试wangwu'+db)
3、fixture结合conftest.py文件使用
- conftest.py写在根目录下面
- conftest.py是专门用于存放fixture的配置文件,名称是固定的,不能变
- 在conftest.py文件里面所有的方法都不需要导包
- conftest.py文件可以有多个,并且多个conftest.py文件里面多个fixture可以被一个方法调用
import pytest
@pytest.fixture(scope="function", autouse=False, name='db')
def exe_database_sql(request):
print('执行sql查询')
yield request.param
print('关闭数据库连接')
from common.common_util import CommonUtil
class TestApi(CommonUtil):
def test_01_get_token(self, db):
print('获取接口统一的鉴权码'+db)
a
4、优先级
setup_method、teardown_method、setup_class、teardown_class、fixture、conftest优先级
优先级高到低:
- 会话:fixture的session
- 类:fixture的class
- 类:setup_class
- 方法:fixture_function
- 方法:setup_method
六、Pytest的执行过程
- 查找当前目录下的conftest.py文件
- 查找当前目录下的pytest.ini文件。找到测试用例的位置
- 查找用例目录下的conftest.py文件
- 查询测试用例的py文件是否有setup_method、teardown_method、setup_class、teardown_class
- 再根据pytest.py文件的测试用例的规则去查找用例并执行
七、Pytest的断言
使用python自己的断言。assert
assert 1=1
assert 'a' in 'abc'
assert flag is True
八、报告生成
pytest结合allure-pytest插件生成美观的报告
- 安装allure-pytest插件
- 下载allure,下载之后解压,解压之后配置环境变量allure下载地址
- 验证allure是否安装成功
- 先在dos中验证:allure --version
- 在pycharm验证(如果失败,需重启Pycharm)
- 生成allure报告
- 生成临时的json报告,在pytest.ini文件里面 加入以下内容
addopts = -vs --alluredir=./temp --clean-allure
# addopts = -vs --alluredir=./temp 生成临时报告
# --clean-allure 清空临时报告
- 生成正式的allure报告,在main函数里面加入以下内容
os.system("allure generate ./temps -o ./reports --clean")
- 定制化