项目目录结构
│ .coverage
│ config.py
│ manage.py
│ README.md
│ requirements.txt
│ setup.cfg
│ tree.txt
├─instance
├─migrations
├─mytools
│ │ extensions.py
│ │ logger.py
│ │ models.py
│ │ __init__.py
│ │
│ ├─logs
│ │ assistant.log
├─tests
│ │ conftest.py
│ │ test_db.py
│ │ test_scope.py
│ │
│ ├─data
│ │ setup.sql
│ │ teardown.sql
借助功能齐全的pytest测试框架,编写了一套mysql数据表的测试框架,实现了数据与用例分离,数据批量初始化、销毁,简单易用。
核心模块conftest.py简介
conftest.py
#!/usr/bin/env python
# encoding: utf-8
"""
@author: wanwei
@license: (C) Copyright 2013-2017, Node Supply Chain Manager Corporation Limited.
@software: pycharm
@file: conftest.py
@time: 2019/9/24 11:02
@desc:
"""
import pytest
import os
import sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../')))
from mytools import create_app
from mytools.extensions import db
# 加载数据初始化SQL语句文件
with open(os.path.join(os.path.dirname(__file__), './data/setup.sql'), 'rb') as f:
_setup_sql = f.readlines()
# 销毁数据SQL语句文件
with open(os.path.join(os.path.dirname(__file__), './data/teardown.sql'), 'rb') as f:
_teardown_sql = f.readlines()
@pytest.fixture(scope='session')
def setup(request):
def teardown():
print("用例执行结束..................................")
request.addfinalizer(teardown)
print("用例执行开始..................................")
@pytest.fixture(scope="module")
def setup_database(request):
print("setup tb_user开始执行...............")
# ----------FLASK-SQLALCHEMY数据库配置---------#
USERNAME = 'root'
PASSWORD = '123456'
HOST = '127.0.0.1'
DB = 'mockservertest'
SQLALCHEMY_DATABASE_URI = 'mysql+mysqlconnector://%s:%s@%s/%s' % (USERNAME, PASSWORD, HOST, DB)
# 创建应用实例
app = create_app({
'TESTING': True,
# 数据库文件存放路径
'SQLALCHEMY_DATABASE_URI': SQLALCHEMY_DATABASE_URI,
'SQLALCHEMY_TRACK_MODIFICATIONS': False,
'SQLALCHEMY_ECHO': False
})
def teardown_database():
print("teardown tb_user开始执行...............")
with app.app_context():
#初始化数据库模型
db.create_all()
for sql in _teardown_sql:
if sql:
db.engine.execute(sql.decode('utf-8'))
request.addfinalizer(teardown_database)
with app.app_context():
for sql in _setup_sql:
if sql:
db.engine.execute(sql.decode('utf-8'))
@pytest.fixture(scope='function')
def setup_function(request):
def teardown_function():
print("teardown_function called...")
request.addfinalizer(teardown_function) # 内嵌函数做teardown操作
print("setup_function called...")
@pytest.fixture(scope='module')
def setup_test(request):
def teardown_test():
print("teardown_module called...")
request.addfinalizer(teardown_test)
print("setup_module called...")
从上面的代码来看,fixture具备以下优势:
- 命名方式灵活,不局限于setup和teardown这几个命名,装饰器@pytest.fixture下可以自定义fixture函数,命名可以根据使用场景来自定义
- conftest.py 配置里可以实现数据共享,不需要import就能自动找到一些配置,运行pytest,即可自动导入
- scope=“module” 可以实现多个.py跨文件共享前置, 一次引入,该用例.py文件调用一次
- scope=“session” 一次引入,多个.py跨文件使用一个session来完成多个用例模块py文件的数据初始化和销毁
conftest.py配置需要注意以下点:
- conftest.py配置脚本名称是固定的,不能改名称,即名称必须是conftest.py
- conftest.py与运行的用例要在同一个pakage下,并且有__init__.py文件
- 不需要import导入 conftest.py,pytest用例会自动查找
数据初始化文件
│ ├─data
│ │ setup.sql
│ │ teardown.sql
借助conftest.py可轻松实现数据与用例分离
setup.sql
INSERT INTO `tb_user`(password_hash,username,lastseen) VALUES ('pbkdf2:sha256:150000$3mEwvVlo$8f98a0ba2576529e7cd379eaea723bd2195ad1882d480150ff4736a5ed05a260', 'wanwei', '2019-9-18 14:59:43');
INSERT INTO `tb_user`(password_hash,username,lastseen) VALUES ('pbkdf2:sha256:150000$P2sZt9ih$d4ef4d800c4ae7a139fae3a389bbddab6789e7524bd976ab5914945bc52d0f59', 'wanwei2', '2019-9-23 20:08:49');
teardown.sql
DELETE FROM `tb_user` WHERE username='wanwei';
DELETE FROM `tb_user` WHERE username='wanwei2';
测试用例
测试用例具备以下特点:
- 测试用例文件以test_开头或以_test结尾
- 引入fixture,只需在测试函数参数列表中传入fixture名称即可
test_db.py
#!/usr/bin/env python
# encoding: utf-8
"""
@author: wanwei
@license: (C) Copyright 2013-2017, Node Supply Chain Manager Corporation Limited.
@software: pycharm
@file: test_db.py
@time: 2019/9/24 13:46
@desc:
"""
from mytools.models import User
from mytools.extensions import db
from mytools import app
def test_password_hash(setup):
"""测试密码加密"""
u = User(username="wanwei3")
u.set_password("sdfg")
assert u.check_password("sdfg") is True
assert u.check_password("huwe") is False
def test_follow(setup_database):
"""测试关注函数"""
with app.app_context():
user1 = User.query.filter_by(username='wanwei').first()
user2 = User.query.filter_by(username='wanwei2').first()
assert user1.username == 'wanwei','user1 username不正确'
assert user2.username == 'wanwei2','user2 username不正确'
test_scope.py
#!/usr/bin/env python
# encoding: utf-8
"""
@author: wanwei
@license: (C) Copyright 2013-2017, Node Supply Chain Manager Corporation Limited.
@contact: wei_wan@sui.com
@software: pycharm
@file: pytest1.py
@time: 2019/9/29 15:29
@desc:
"""
import pytest
@pytest.mark.recommend
def test_1(setup_function):
print("test_1 called...")
def test_2(setup_test):
# assert 1 != 1
print("test_2 called...")
def test_3(setup_test):
print("test_3 called...")
测试配置文件setup.cfg
- 与应用 app 同级,用于配置测试参数;
- filterwarnings 用于忽略废弃提示;
- testpaths 用于定义测试脚本所在目录;
- source 设置 coverage 测试时的应用名称。
setup.cfg
[tool:pytest]
filterwarnings =
error
ignore::DeprecationWarning
testpaths = tests
[coverage:run]
branch = True
source = mytools
测试运行
- pytest,命令行输出一串点
- pytest -v,命令行输出每个用例的列表
- pytest -s,命令行可以输出用例执行的详细print函数打印信息
- pytest 用例文件名,可以单独运行用例模块
- pytest ./tests/test_scope.py::test_2,单独运行某个测试用例
使用coverage模块统计测试覆盖率
(mockenv) E:\技术资料\接口框架\datatestdemo>pytest ./tests/test_scope.py::test_2
============================================================================================ test session starts ============================================================================================
platform win32 -- Python 3.5.2, pytest-3.9.2, py-1.7.0, pluggy-0.8.0
rootdir: E:\技术资料\接口框架\datatestdemo, inifile: setup.cfg
plugins: metadata-1.7.0, html-1.19.0, cov-2.7.1, allure-adaptor-1.7.10
collected 1 item
tests\test_scope.py . [100%]
========================================================================================= 1 passed in 0.07 seconds ==========================================================================================
(mockenv) E:\技术资料\接口框架\datatestdemo>
(mockenv) E:\技术资料\接口框架\datatestdemo>
(mockenv) E:\技术资料\接口框架\datatestdemo>coverage run -m pytest
============================================================================================ test session starts ============================================================================================
platform win32 -- Python 3.5.2, pytest-3.9.2, py-1.7.0, pluggy-0.8.0
rootdir: E:\技术资料\接口框架\datatestdemo, inifile: setup.cfg
plugins: metadata-1.7.0, html-1.19.0, cov-2.7.1, allure-adaptor-1.7.10
collected 5 items
tests\test_db.py .. [ 40%]
tests\test_scope.py ... [100%]
========================================================================================= 5 passed in 0.65 seconds ==========================================================================================
(mockenv) E:\技术资料\接口框架\datatestdemo>coverage report
Name Stmts Miss Branch BrPart Cover
---------------------------------------------------------
mytools\__init__.py 19 0 4 0 100%
mytools\extensions.py 7 0 0 0 100%
mytools\logger.py 13 1 2 1 87%
mytools\models.py 29 5 0 0 83%
---------------------------------------------------------
TOTAL 68 6 6 1 91%
生成html报告
coverage html
在htmlcov目录中生成HTML报告,直接打开htmlcov/index.html 即可查看用例覆盖情况:
项目地址:
https://github.com/wanwei890116/Apitest
参考:
pytest文档5-fixture之conftest.py
https://www.cnblogs.com/yoyoketang/p/9390073.html