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

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

測試 Flask 應用

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

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

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

應用

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

測試骨架

為了測試應用,我們添加了一個新的模塊 (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() 方法中會創(chuàng)建一個新的測試客戶端并初始化一個新的數(shù)據(jù)庫。在每個獨立的測試函數(shù)運行前都會調(diào)用這個方法。 tearDown() 方法的功能是在測試結束后關閉文件,并在文件系統(tǒng)中刪除數(shù)據(jù)庫文件。另外在設置中 TESTING 標志開啟的,這意味著在請求時關閉 錯誤捕捉,以便于在執(zhí)行測試請求時得到更好的錯誤報告。

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

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

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

$ python flaskr_tests.py

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

OK

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

第一個測試

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

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 就會自動識別這些是用于測試的函數(shù)并運行它們。

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

再次運行測試,會看到通過了一個測試:

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

OK

登錄和注銷

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

在 FlaskrTestCase 類中添加以下兩個方法:

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 ,但標題不可以。應用設計思路就是這樣的。

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

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

OK

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

其他測試技巧

除了使用上述測試客戶端外,還可以在 with 語句中使用 test_request_context() 方法來臨時激活一個請求環(huán)境。在這個 環(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)境綁定的對象也可以這樣使用。

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

注意,在測試請求環(huán)境中 before_request() 函數(shù)和 after_request() 函數(shù)不會被自動調(diào)用。但是當調(diào)試請求環(huán)境離開 with 塊時會執(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ù)應用需要打開其他類似東西。

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

app = flask.Flask(__name__)

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

這個例子中的情況基本沒有用處,因為在這種情況下可以直接開始使用測試客戶端。

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

New in version 0.10.

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

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

在測試時可以很很方便地重載用戶而不用改動代碼。可以先象下面這樣鉤接 flask.appcontext_pushed 信號:

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.

有時候這種情形是有用的:觸發(fā)一個常規(guī)請求,但是保持環(huán)境以便于做一點額外的事情。 在 Flask 0.4 之后可以在 with 語句中使用 test_client() 來 實現(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 會出錯失敗。因為無法在請求之外訪問 request 。

訪問和修改會話

New in version 0.8.

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

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

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

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

    # 運行到這里時,會話已被保存

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

? Copyright 2013, Armin Ronacher. Created using Sphinx.