Testing Flask Applications
按照pytest
$ pip install pytest
从https://github.com/pallets/flask/tree/1.1.2/examples/tutorial下载示例。然后进入到example/tutorial目录。
在源码目录创建一个tests目录,然后在其下创建一个文件test_flaskr.py来保存测试代码。pytest会自动识别以test_*.py
格式的文件名,并从中提取测试用例。
接着创建一个pytest的fixture:
import os
import tempfile
import pytest
from flaskr import flaskr
@pytest.fixture
def client():
db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp()
flaskr.app.config['TESTING'] = True
with flaskr.app.test_client() as client:
with flaskr.app.app_context():
flaskr.init_db()
yield client
os.close(db_fd)
os.unlink(flaskr.app.config['DATABASE'])
client()作为一个fixture,在执行每个用例前都会被调用。所以数据库被放在了临时的文件,并且TESTING选项被开启。然后client()会被调用两次,第一次yield一个app.test_client示例,第二次应该是在结束的时候,关掉数据库文件。
The First Test
在test_flaskr添加一个测试函数:
def test_empty_db(client):
"""Start with a blank database."""
rv = client.get('/')
assert b'No entries here so far' in rv.data
Logging In and Out
首先准备两个工具函数:
def login(client, username, password):
return client.post('/login', data=dict(
username=username,
password=password
), follow_redirects=True)
def logout(client):
return client.get('/logout', follow_redirects=True)
然后测试登录和退出:
def test_login_logout(client):
"""Make sure login and logout works."""
rv = login(client, flaskr.app.config['USERNAME'], flaskr.app.config['PASSWORD'])
assert b'You were logged in' in rv.data
rv = logout(client)
assert b'You were logged out' in rv.data
rv = login(client, flaskr.app.config['USERNAME'] + 'x', flaskr.app.config['PASSWORD'])
assert b'Invalid username' in rv.data
rv = login(client, flaskr.app.config['USERNAME'], flaskr.app.config['PASSWORD'] + 'x')
assert b'Invalid password' in rv.data
Test Adding Messages
测试添加消息:
def test_messages(client):
"""Test that messages work."""
login(client, flaskr.app.config['USERNAME'], flaskr.app.config['PASSWORD'])
rv = client.post('/add', data=dict(
title='<Hello>',
text='<strong>HTML</strong> allowed here'
), follow_redirects=True)
assert b'No entries here so far' not in rv.data
assert b'<Hello>' in rv.data
assert b'<strong>HTML</strong> allowed here' in rv.data
Other Testing Tricks
除了使用test client之外,也可以使用test_request_context(),有了这个就可以访问request, g, 以及session对象:
import flask
app = flask.Flask(__name__)
with app.test_request_context('/?name=Peter'):
assert flask.request.path == '/'
assert flask.request.args['name'] == 'Peter'
这种情况下, before_request() 和after_request() 不会被运行,但是teardown_request() 会被with语块自动运行,如果真的需要before_request() 被调用(比如需要创建数据库链接),那么可以使用preprocess_request() :
app = flask.Flask(__name__)
with app.test_request_context('/?name=Peter'):
app.preprocess_request()
...
同样的,通过显示调用process_response()可以触发after_request() :
app = flask.Flask(__name__)
with app.test_request_context('/?name=Peter'):
resp = Response('...')
resp = app.process_response(resp)
...
你也可以使用Application Factories
Faking Resources and Context
对于保存在flask.g里面的局部全局对象,可以通过 flask.appcontext_pushed 来模拟:
from contextlib import contextmanager
from flask import appcontext_pushed, g
@contextmanager
def user_set(app, user):
def handler(sender, **kwargs):
g.user = user
with appcontext_pushed.connected_to(handler, app):
yield
from flask import json, jsonify
@app.route('/users/me')
def users_me():
return jsonify(username=g.user.username)
with user_set(app, my_user):
with app.test_client() as c:
resp = c.get('/users/me')
data = json.loads(resp.data)
self.assert_equal(data['username'], my_user.username)
Keeping the Context Around
如果对test_client()不适用with语句,那么context将不可用:
app = flask.Flask(__name__)
with app.test_client() as c:
rv = c.get('/?tequila=42')
assert request.args['tequila'] == '42'
上面assert会失败,因为那个时刻context已经消失。
Accessing and Modifying Sessions
你可以这样写测试:
with app.test_client() as c:
rv = c.get('/')
assert flask.session['foo'] == 42
但是上面的代码无法修改会话内容,一个变通办法是使用session_transaction():
with app.test_client() as c:
with c.session_transaction() as sess:
sess['a_key'] = 'a value'
# once this is reached the session was stored and ready to be used by the client
c.get(...)
Testing JSON APIs
from flask import request, jsonify
@app.route('/api/auth')
def auth():
json_data = request.get_json()
email = json_data['email']
password = json_data['password']
return jsonify(token=generate_token(email, password))
with app.test_client() as c:
rv = c.post('/api/auth', json={
'email': 'flask@example.com', 'password': 'secret'
})
json_data = rv.get_json()
assert verify_token(email, json_data['token'])
在post函数中设置json参数可以将http内容类型设置为appliation/json,通过get_json可以从request或者response中获取json数据。
Testing CLI Commands¶
略
example/tutorial中例子的执行步骤
使用venv创建一个虚拟环境,并调用其activate.bat进入(假设在Windows的CMD环境):
python3 -m venv vv
vv\Scripts\activate.bat
venv是python3.3引入到标准库的。
然后在虚拟环境vv中安装flaskr,以及pytest:
pip install -e .
pip install ".[test]" # 等同于pip install pytest
然后运行pytest以及coverage
pytest
pip install coverage
coverage run -m pytest
coverage repor
其他
windows竟然自带一个Python,在C:\Windows\py.exe。
(草草结束)