框架选择:
根据 https://blog.zengrong.net/pos…
得知:
flask项目本身使用的是pytest
nose是对标准库unittest的封装,现在比较流行,但文档没有pytest做的好,且近几年一直处于维护状态没有更新。
Flask-Testing flask扩展
最终选择:pytest
pytest
特点:
- 非常容易上手,入门简单,文档丰富,文档中有很多实例可以参考
- 能够支持简单的单元测试和复杂的功能测试
- 支持参数化
- 执行测试过程中可以将某些测试跳过,或者对某些预期失败的case标记成失败
- 支持重复执行失败的case
- 支持运行由nose, unittest编写的测试case
- 具有很多第三方插件,并且可以自定义扩展
- 方便的和持续集成工具集成
在命令行输入如下命令检查pytest是否已安装
py.test --version
如果没有
pip install -U pytest
第一个例子:测试函数
# content of test_sample.py
def func(x):
return x+1
def test_func():
assert func(3) == 5
运行:
执行测试时需要下面几步:
- 从命令行进入测试文件所在目录,pytest会在该目录中寻找以test开头的文件
- 找到测试文件,进入测试文件中寻找以test_开头的函数并执行
- 测试函数以断言assert结尾
$ py.test
============================= test session starts ==============================
platform darwin -- Python 3.5.1, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
rootdir: /Users/fc/project/test/pytest_sample, inifile:
collected 1 items
test_sample.py F
=================================== FAILURES ===================================
__________________________________ test_func ___________________________________
def test_func():
> assert func(3) == 5
E assert 4 == 5
E + where 4 = func(3)
test_sample.py:9: AssertionError
=========================== 1 failed in 0.01 seconds ===========================
第二个例子: 测试类
# content of test_class.py
class TestClass(object):
def test_one(self):
x = 'this'
assert 'h' in x
def test_two(self):
x = 'hello'
assert hasattr(x, 'check')
运行
下面的-q是 quiet的意思,就是忽略一些很细节的信息
使用测试类时,注意下面几点:
- 测试类所在的文件以test_开头
- 测试类以Test开头,并且不能带有__init__方法
- 类中测试函数以test_开头
- 测试函数以assert断言结尾
bogon:pytest_sample fc$ py.test -q test_class.py
.F
=================================== FAILURES ===================================
______________________________ TestClass.test_two ______________________________
self = <test_class.TestClass object at 0x10595b080>
def test_two(self):
x = 'hello'
> assert hasattr(x, 'check')
E assert hasattr('hello', 'check')
test_class.py:11: AssertionError
1 failed, 1 passed in 0.01 seconds
第三个例子:参数化测试函数
import pytest
params = [
(2, 3, 5),
(4, 5, 9),
(6, 7, 12)
]
@pytest.mark.parametrize('a, b, expected', params)
def test_add(a, b, expected):
assert a + b == expected
运行结果
$ py.test -q test_params.py
..F
=================================== FAILURES ===================================
_______________________________ test_add[6-7-12] _______________________________
a = 6, b = 7, expected = 12
@pytest.mark.parametrize('a, b, expected', params)
def test_add(a, b, expected):
> assert a + b == expected
E assert (6 + 7) == 12
test_params.py:12: AssertionError
1 failed, 2 passed in 0.01 seconds
说明:
- params是要进行测试的参数list,其中元素为tuple,每个tuple对应一套参数
- @pytest.mark.parametrize装饰器的第一个参数是一个字符串,不过这个字符串其实是以逗号分隔的一组参数,这个参数就是其所装饰的函数的参数。
- @pytest.mark.parametrize装饰器将params中的参数一套一套拿出来放入其所装饰的函数中执行
第四个例子 fixture params
import pytest
@pytest.fixture(params=[1, 2, 3])
def test_data(request):
return request.param
def test_not_2(test_data):
assert test_data != 2
运行结果:
$ py.test -q fixture_params.py
.F.
======================================= FAILURES =======================================
____________________________________ test_not_2[2] _____________________________________
test_data = 2
def test_not_2(test_data):
> assert test_data != 2
E assert 2 != 2
fixture_params.py:10: AssertionError
1 failed, 2 passed in 0.01 seconds
说明:
- 把一个函数定用@pytest.fixture装饰,那这个函数就是fixture函数
- 一个fixture函数可以被其他测试函数调用,将函数名当作参数即可,fixture的返回值会当作测试函数的参数
- fixture函数中的params字段默认为None,如果有值,则每个值都会调用执行一次
在flask项目中使用pytest
flask应用demo代码
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'hello'
@app.route('/login')
def login():
return 'login'
@app.route('/logout')
def logout():
return 'logout'
@app.errorhandler(404)
def page_not_found():
return '404'
if __name__ == '__main__':
app.run()
flask demo的测试代码
from flaskr import app
class TestClass(object):
def setup_class(self):
"""测试开始时候执行, 用来做准备工作,一般用来初始化资源。"""
app.config['TESTING'] = True # 这将会使得处理请求时的错误捕捉失效,以便于 您在进行对应用发出请求的测试时获得更好的错误反馈。
# 测试客户端将会给我们一个通向应用的简单接口,我们可以激发 对向应用发送请求的测试,并且此客户端也会帮我们记录 Cookie 的 动态。
self.app = app.test_client()
def teardown_class(self):
"""测试结束时执行, 用来做收尾工作, 一般用来关闭资源"""
pass
def test_login(self):
response = self.app.get('/login')
assert b'login' == response.data
def test_logout(self):
response = self.app.get('logout')
assert b'logout' == response.data
def test_index(self):
response = self.app.get('/')
assert b'hello' == response.data