2011-02-17 86 views
23

似乎在瓶通常的做法是這樣開始:如何用SQLAlchemy設置Flask應用程序進行測試?

from flask import Flask 
from flaskext.sqlalchemy import SQLAlchemy 
app = Flask(__name__) 
SQLALCHEMY_DATABASE_URI = 'something' 
app.config.from_object(__name__) 
db = SQLAlchemy(app) 

,然後導入和使用appdb無處不在。但是當你像這樣創建db時,它會從應用中獲取配置,而且看起來這種配置一旦發生就不能被覆蓋。在Flask的網站上有一些關於製作應用程序工廠的網頁,但是如果我這樣做的話,我還不清楚如何在任何地方仍然可以使用appdb

如何編寫腳本來測試具有不同數據庫的Flask應用程序?我應該如何構建我的應用程序才能實現這一點?我必須使用module s嗎?

+3

與此同時,我打算使用app.config.from_envvar,並總是在命令行中指定配置文件,當我以不同的方式運行我的應用程序時。 – 2011-02-17 08:36:45

+0

這確實看起來像一個解決方案,但如果你忘記和運行測試與錯誤的數據庫... – 2012-10-02 23:28:49

回答

9

你本能地使用環境變量是正確的。但是,使用錯誤的db運行單元測試存在一些危險。此外,您可能不希望connect_db與每個請求和您想要使用的任何地方db。您可以使用明確設置的配置目錄和環境變量。這是迄今爲止我所見過的最好的。

run.py 
shell.py 

config/__init__.py 
config/test.py 
config/postgres.py 
... 

main/__init__.py 
main/someapp/__init__.py 
main/someapp/models.py 

... 
main/tests/__init__.py 
main/tests/testutils.py 

因此,該配置文件可能是:

# config/test.py 
SQLALCHEMY_DATABASE_URI = "sqlite://" 

# config/postgres.py 
SQLALCHEMY_DATABASE_URI = 'postgresql://user:[email protected]/somedb' 

所以,我可以明確地設置數據庫在我的基地測試用例:

import os 
from flask.ext.testing import TestCase 

os.environ["DIAG_CONFIG_MODULE"] = "config.test" 
from main import app, db 


class SQLAlchemyTest(TestCase): 

    def create_app(self): 
     return app 

    def setUp(self): 
     db.create_all() 

    def tearDown(self): 
     db.session.remove() 
     db.drop_all() 

然後,main/__init__.py,對我來說:

import os 

from flask import Flask, render_template, g 
from flask.ext.sqlalchemy import SQLAlchemy 

# by default, let's use a DB we don't care about 
# but, we can override if we want 
config_obj = os.environ.get("DIAG_CONFIG_MODULE", "config.test") 
app = Flask(__name__) 
app.config.from_object(config_obj) 
db = SQLAlchemy(app) 

@app.before_request 
def before_request(): 
    g.db = db 
    g.app = app 

# ... 
@app.route('/', methods=['GET']) 
def get(): 
    return render_template('home.html') 
# ...  
from main.someapp.api import mod as someappmod 
app.register_blueprint(someappmod) 

然後,在其他文件,在那裏我知道我要運行的配置,有可能:

# run.py 
import os 
os.environ["DIAG_CONFIG_MODULE"] = "config.postgres" 
from main import app 
app.run(debug=True) 

# shell.py 
import os 
os.environ["DIAG_CONFIG_MODULE"] = "config.postgres" 

from main import app, db 
from main.symdiag.models import * 
from main.auth.models import * 
print sorted(k for k in locals().keys() if not k.startswith("_")) 
import IPython 
IPython.embed() 

也許..迄今爲止最好的:P。

6

您不想在導入時連接到數據庫。繼續在導入時配置您的應用程序,因爲在嘗試測試或運行您的應用程序之前,您始終可以在測試中調整配置。在下面的示例中,您將在使用應用程序配置的某些函數背後連接數據庫連接,因此在unittest中,您可以實際更改數據庫連接以指向其他文件,然後繼續並在您的設置中明確連接。

假設你有一個包含myapp.py它看起來像一個MYAPP包:

# myapp/myapp.py 
from __future__ import with_statement 
from sqlite3 import dbapi2 as sqlite3 
from contextlib import closing 
from flask import Flask, request, session, g, redirect, url_for, abort, \ 
    render_template, flash 

# configuration 
DATABASE = '/tmp/flaskr.db' 
DEBUG = True 
SECRET_KEY = 'development key' 
USERNAME = 'admin' 
PASSWORD = 'default' 

# create our little application :) 
app = Flask(__name__) 
app.config.from_object(__name__) 
app.config.from_envvar('MYAPP_SETTINGS', silent=True) 

def connect_db(): 
    """Returns a new connection to the database.""" 
    return sqlite3.connect(app.config['DATABASE']) 


def init_db(): 
    """Creates the database tables.""" 
    with closing(connect_db()) as db: 
     with app.open_resource('schema.sql') as f: 
      db.cursor().executescript(f.read()) 
     db.commit() 


@app.before_request 
def before_request(): 
    """Make sure we are connected to the database each request.""" 
    g.db = connect_db() 


@app.after_request 
def after_request(response): 
    """Closes the database again at the end of the request.""" 
    g.db.close() 
    return response 

@app.route('/') 
def show_entries(): 
    cur = g.db.execute('select title, text from entries order by id desc') 
    entries = [dict(title=row[0], text=row[1]) for row in cur.fetchall()] 
    return render_template('show_entries.html', entries=entries) 

if __name__=="__main__": 
    app.run() 

您的測試文件的myapp/test_myapp.py看起來就像這樣:

import os 
import myapp 
import unittest 
import tempfile 

class MyappTestCase(unittest.TestCase): 

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

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

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

當然,如果你」喜歡使用SQLAlchemy,你必須適當地更新connect_db和init_db函數,但希望你能明白。

2

首先,不是直接在腳本中實例化Flask應用程序,而是使用application factory。這意味着你創建一個函數,將你的配置文件作爲參數,並返回實例化的應用程序對象。然後,創建不帶參數的全局SQLAlchemy對象,並在創建應用程序時對其進行配置,即as explained here

db = SQLAlchemy() 

def create_app(configfile): 
    app = Flask(__name__) 

    app.config.from_pyfile(config, silent=True) 
    db.init_app(app) 

    # create routes, etc. 

    return app 

運行應用程序,你只需做這樣的事情:

app = create_app('config.py') 
app.run() 

要運行單元測試,你可以這樣做:

class Test(TestCase): 
    def setUp(self): 
     # init test database, etc. 
     app = create_app('test_config.py') 
     self.app = app.test_client() 
    def tearDown(self): 
     # delete test database, etc. 

就我而言,我使用SQLAlchemy直接使用scoped_session而不是Flask-SQLAlchemy。 我做了同樣的事情,但用Lazy SQLAlchemy setup

相關問題