在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Python/ 測試 Flask 應(yīng)用
應(yīng)用環(huán)境
配置管理
大型應(yīng)用
可插撥視圖
Flask 方案
在 Shell 中使用 Flask
針對高級(jí)程序員的前言
使用藍(lán)圖的模塊化應(yīng)用
部署方式
信號(hào)
排除應(yīng)用錯(cuò)誤
模板
請求環(huán)境
掌握應(yīng)用錯(cuò)誤
測試 Flask 應(yīng)用
前言
教程
安裝
快速上手
Flask 擴(kuò)展

測試 Flask 應(yīng)用

未經(jīng)測試的小貓,肯定不是一只好貓。

這句話的出處不詳(譯者注:這句是譯者獻(xiàn)給小貓的),也不一定完全正確,但是基本上是正確的。未經(jīng)測試的應(yīng)用難于改進(jìn)現(xiàn)有的代碼,因此其開發(fā)者會(huì)越改進(jìn)越抓狂。反之, 經(jīng)過自動(dòng)測試的代碼可以安全的改進(jìn),并且如果可以測試過程中立即發(fā)現(xiàn)錯(cuò)誤。

Flask 提供的測試渠道是公開 Werkzeug 的 Client ,為你 處理本地環(huán)境。你可以結(jié)合這個(gè)渠道使用你喜歡的測試工具。本文使用的測試工具是隨著 Python 一起安裝好的 unittest 包。

應(yīng)用

首先,我們需要一個(gè)用來測試的應(yīng)用。我們將使用教程中的應(yīng)用。如果你還沒有這個(gè)應(yīng)用,可以下載示例代碼 。

測試骨架

為了測試應(yīng)用,我們添加了一個(gè)新的模塊 (flaskr_tests.py) 并創(chuàng)建了如下測試骨架:

import os
import flaskr
import unittest
import tempfile

class FlaskrTestCase(unittest.TestCase):

    def setUp(self):
        self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp()
        flaskr.app.config['TESTING'] = True
        self.app = flaskr.app.test_client()
        flaskr.init_db()

    def tearDown(self):
        os.close(self.db_fd)
        os.unlink(flaskr.app.config['DATABASE'])

if __name__ == '__main__':
    unittest.main()

setUp() 方法中會(huì)創(chuàng)建一個(gè)新的測試客戶端并初始化一個(gè)新的數(shù)據(jù)庫。在每個(gè)獨(dú)立的測試函數(shù)運(yùn)行前都會(huì)調(diào)用這個(gè)方法。 tearDown() 方法的功能是在測試結(jié)束后關(guān)閉文件,并在文件系統(tǒng)中刪除數(shù)據(jù)庫文件。另外在設(shè)置中 TESTING 標(biāo)志開啟的,這意味著在請求時(shí)關(guān)閉 錯(cuò)誤捕捉,以便于在執(zhí)行測試請求時(shí)得到更好的錯(cuò)誤報(bào)告。

測試客戶端會(huì)給我們提供一個(gè)簡單的應(yīng)用接口。我們可以通過這個(gè)接口向應(yīng)用發(fā)送測試請求。客戶端還可以追蹤 cookies 。

因?yàn)?SQLite3 是基于文件系統(tǒng)的,所以我們可以方便地使用臨時(shí)文件模塊來創(chuàng)建一個(gè)臨時(shí)數(shù)據(jù)庫并初始化它。 mkstemp() 函數(shù)返回兩個(gè)東西:一個(gè)低級(jí)別的文件 句柄和一個(gè)隨機(jī)文件名。這個(gè)文件名后面將作為我們的數(shù)據(jù)庫名稱。我們必須把句柄保存到 db_fd 中,以便于以后用 os.close() 函數(shù)來關(guān)閉文件。

如果現(xiàn)在進(jìn)行測試,那么會(huì)輸出以下內(nèi)容:

$ python flaskr_tests.py

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

雖然沒有運(yùn)行任何實(shí)際測試,但是已經(jīng)可以知道我們的 flaskr 應(yīng)用沒有語法錯(cuò)誤。 否則在導(dǎo)入時(shí)會(huì)引發(fā)異常并中斷運(yùn)行。

第一個(gè)測試

現(xiàn)在開始測試應(yīng)用的功能。當(dāng)我們訪問應(yīng)用的根 URL ( / )時(shí)應(yīng)該顯示 “ No entries here so far ”。我們新增了一個(gè)新的測試方法來測試這個(gè)功能:

class FlaskrTestCase(unittest.TestCase):

    def setUp(self):
        self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp()
        self.app = flaskr.app.test_client()
        flaskr.init_db()

    def tearDown(self):
        os.close(self.db_fd)
        os.unlink(flaskr.app.config['DATABASE'])

    def test_empty_db(self):
        rv = self.app.get('/')
        assert 'No entries here so far' in rv.data

注意,我們的調(diào)試函數(shù)都是以 test 開頭的。這樣 unittest 就會(huì)自動(dòng)識(shí)別這些是用于測試的函數(shù)并運(yùn)行它們。

通過使用 self.app.get ,可以向應(yīng)用的指定 URL 發(fā)送 HTTP GET 請求,其返回的是一個(gè) ~flask.Flask.response_class 對象。我們可以使用 data 屬性來檢查應(yīng)用的返回值(字符串類型)。在本例中,我們檢查輸出是否包含 'No entries here so far' 。

再次運(yùn)行測試,會(huì)看到通過了一個(gè)測試:

$ python flaskr_tests.py
.
----------------------------------------------------------------------
Ran 1 test in 0.034s

OK

登錄和注銷

我們應(yīng)用的主要功能必須登錄以后才能使用,因此必須測試應(yīng)用的登錄和注銷。測試的 方法是使用規(guī)定的數(shù)據(jù)(用戶名和密碼)向應(yīng)用發(fā)出登錄和注銷的請求。因?yàn)榈卿浐妥N后會(huì)重定向到別的頁面,因此必須告訴客戶端使用 follow_redirects 追蹤重定向。

在 FlaskrTestCase 類中添加以下兩個(gè)方法:

def login(self, username, password):
    return self.app.post('/login', data=dict(
        username=username,
        password=password
    ), follow_redirects=True)

def logout(self):
    return self.app.get('/logout', follow_redirects=True)

現(xiàn)在可以方便地測試登錄成功、登錄失敗和注銷功能了。下面為新增的測試代碼:

def test_login_logout(self):
    rv = self.login('admin', 'default')
    assert 'You were logged in' in rv.data
    rv = self.logout()
    assert 'You were logged out' in rv.data
    rv = self.login('adminx', 'default')
    assert 'Invalid username' in rv.data
    rv = self.login('admin', 'defaultx')
    assert 'Invalid password' in rv.data

測試增加條目功能

我們還要測試增加條目功能。添加以下測試代碼:

def test_messages(self):
    self.login('admin', 'default')
    rv = self.app.post('/add', data=dict(
        title='<Hello>',
        text='<strong>HTML</strong> allowed here'
    ), follow_redirects=True)
    assert 'No entries here so far' not in rv.data
    assert '&lt;Hello&gt;' in rv.data
    assert '<strong>HTML</strong> allowed here' in rv.data

這里我們檢查了博客內(nèi)容中允許使用 HTML ,但標(biāo)題不可以。應(yīng)用設(shè)計(jì)思路就是這樣的。

運(yùn)行測試,現(xiàn)在通過了三個(gè)測試:

$ python flaskr_tests.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.332s

OK

關(guān)于更復(fù)雜的 HTTP 頭部和狀態(tài)碼測試參見 MiniTwit 示例 。這個(gè)示例的源代碼中 包含了更大的測試套件。

其他測試技巧

除了使用上述測試客戶端外,還可以在 with 語句中使用 test_request_context() 方法來臨時(shí)激活一個(gè)請求環(huán)境。在這個(gè) 環(huán)境中可以像以視圖函數(shù)中一樣操作 request 、g 和 session 對象。示例:

app = flask.Flask(__name__)

with app.test_request_context('/?name=Peter'):
    assert flask.request.path == '/'
    assert flask.request.args['name'] == 'Peter'

其他與環(huán)境綁定的對象也可以這樣使用。

如果你必須使用不同的配置來測試應(yīng)用,而且沒有什么好的測試方法,那么可以考慮使用應(yīng)用工廠(參見應(yīng)用工廠 )。

注意,在測試請求環(huán)境中 before_request() 函數(shù)和 after_request() 函數(shù)不會(huì)被自動(dòng)調(diào)用。但是當(dāng)調(diào)試請求環(huán)境離開 with 塊時(shí)會(huì)執(zhí)行 teardown_request() 函數(shù)。如果需要 before_request() 函數(shù)和正常情況下一樣被調(diào)用,那么你必須調(diào)用 preprocess_request()

app = flask.Flask(__name__)

with app.test_request_context('/?name=Peter'):
    app.preprocess_request()
    ...

在這函數(shù)中可以打開數(shù)據(jù)庫連接或者根據(jù)應(yīng)用需要打開其他類似東西。

如果想調(diào)用 after_request() 函數(shù),那么必須調(diào)用 process_response() ,并把響應(yīng)對象傳遞給它:

app = flask.Flask(__name__)

with app.test_request_context('/?name=Peter'):
    resp = Response('...')
    resp = app.process_response(resp)
    ...

這個(gè)例子中的情況基本沒有用處,因?yàn)樵谶@種情況下可以直接開始使用測試客戶端。

偽造資源和環(huán)境

New in version 0.10.

通常情況下,我們會(huì)把用戶認(rèn)證信息和數(shù)據(jù)庫連接儲(chǔ)存到應(yīng)用環(huán)境或者 flask.g 對象中,并在第一次使用前準(zhǔn)備好,然后在斷開時(shí)刪除。假設(shè)應(yīng)用中得到當(dāng)前用戶的代碼如下:

def get_user():
    user = getattr(g, 'user', None)
    if user is None:
        user = fetch_current_user_from_database()
        g.user = user
    return user

在測試時(shí)可以很很方便地重載用戶而不用改動(dòng)代碼。可以先象下面這樣鉤接 flask.appcontext_pushed 信號(hào):

from contextlib import contextmanager
from flask import appcontext_pushed

@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)

保持環(huán)境

New in version 0.4.

有時(shí)候這種情形是有用的:觸發(fā)一個(gè)常規(guī)請求,但是保持環(huán)境以便于做一點(diǎn)額外的事情。 在 Flask 0.4 之后可以在 with 語句中使用 test_client() 來 實(shí)現(xiàn):

app = flask.Flask(__name__)

with app.test_client() as c:
    rv = c.get('/?tequila=42')
    assert request.args['tequila'] == '42'

如果你在沒有 with 的情況下使用 test_client() ,那么 assert 會(huì)出錯(cuò)失敗。因?yàn)闊o法在請求之外訪問 request 。

訪問和修改會(huì)話

New in version 0.8.

有時(shí)候在測試客戶端中訪問和修改會(huì)話是非常有用的。通常有兩方法。如果你想測試會(huì)話中 的鍵和值是否正確,你可以使用 flask.session:

with app.test_client() as c:
    rv = c.get('/')
    assert flask.session['foo'] == 42

但是這個(gè)方法無法修改會(huì)話或在請求發(fā)出前訪問會(huì)話。自 Flask 0.8 開始,我們提供了 “會(huì)話處理”,用打開測試環(huán)境中會(huì)話和修改會(huì)話,最后保存會(huì)話。處理后的會(huì)話獨(dú)立于 后端實(shí)際使用的會(huì)話:

with app.test_client() as c:
    with c.session_transaction() as sess:
        sess['a_key'] = 'a value'

    # 運(yùn)行到這里時(shí),會(huì)話已被保存

注意在這種情況下必須使用 sess 對象來代替 flask.session 代理。 sess 對象本身可以提供相同的接口。

? Copyright 2013, Armin Ronacher. Created using Sphinx.