2013-07-22 11 views
14

Ho un'applicazione Web funzionante su Flask con SqlAlchemy per la moderazione delle notizie, ha alcuni metodi API per gestire le richieste di moderazione, come approvare, negare le notizie attualmente selezionate, elencarle, ecc. Voglio scrivere unit test a questi metodi, e li ho fatti funzionare, ma non capisco, come implementare l'esecuzione di tutte le richieste che faccio da casi di test in una sessione db, in modo da poter rimuovere tutte le modifiche al database. O c'è un altro modo più pulito o corretto per farlo? Ho scoperto che forse tutto ciò di cui ho bisogno è "scoped_session" in SqlAlchemy, ma tutti i miei tentativi di implementarlo sono falliti. Se è il modo corretto, per favore, dimmi dove usare queste righe di codice (nelle impostazioni, o nel metodo set_up del caso di test).flacone di prova sql alchimia

from sqlalchemy.orm import scoped_session 
from sqlalchemy.orm import sessionmaker 
session_factory = sessionmaker() 
Session = scoped_session(session_factory) 
+0

stai usando sqlalchemy direttamente o l'estensione flask-sqlalchemy? – codegeek

+0

sqlalchemy direttamente –

risposta

12

Suggerisco di utilizzare l'estensione Flask-Testing. Questa è un'estensione approvata che ti consente di eseguire il test dell'unità come desideri. Ha anche una sezione specifica per SQLAlchemy.

Test con SQLAlchemy

Questo copre un paio di punti se si utilizza Flask-Test con SQLAlchemy. Si presume che si stia utilizzando l'estensione Flask-SQLAlchemy, ma in caso contrario gli esempi non dovrebbero essere troppo difficili da adattare alla propria particolare configurazione.

Innanzitutto, assicurarsi di impostare l'URI del database su un valore diverso dal proprio database di produzione! In secondo luogo, di solito è una buona idea per creare e rilasciare le tabelle con l'esecuzione del test, al fine di garantire i test puliti:"

from flask.ext.testing import TestCase 

from myapp import create_app, db 

class MyTest(TestCase): 

    SQLALCHEMY_DATABASE_URI = "sqlite://" 
    TESTING = True 

    def create_app(self): 

     # pass in test configuration 
     return create_app(self) 

    def setUp(self): 

     db.create_all() 

    def tearDown(self): 

     db.session.remove() 
     db.drop_all() 
+10

Mi manca qualcosa dato gli upvotes su questa domanda, ma l'implementazione di 'create_app' è piuttosto importante qui? La documentazione collegata non spiega cosa dovrebbe fare "myapp.create_app' (ad esempio, in relazione al db). – ForeverWintr

+0

create_app Penso che dovremmo restituire l'app come nella tua app.py - ad es. app = Flask (__ name__). Poiché non desidero una configurazione diversa per il test e la produzione, impongo semplicemente l'app dal mio spazio dei nomi principale e la restituisco in create_app, e funziona perfettamente. –

9

Questo è il modo in cui sono stato in esecuzione test di unità di recente sto assumendo da quando. stanno usando SQLAlchemy che si sta utilizzando classi del modello. sono anche supponendo che tutte le tabelle sono definiti come classi del modello SQLAlchemy.

from flask import Flask 
import unittest 

from app import db 
from app.models import Log 
from constants import test_logs 

class appDBTests(unittest.TestCase): 

    def setUp(self): 
     """ 
     Creates a new database for the unit test to use 
     """ 
     self.app = Flask(__name__) 
     db.init_app(self.app) 
     with self.app.app_context(): 
      db.create_all() 
      self.populate_db() # Your function that adds test data. 

    def tearDown(self): 
     """ 
     Ensures that the database is emptied for next unit test 
     """ 
     self.app = Flask(__name__) 
     db.init_app(self.app) 
     with self.app.app_context(): 
      db.drop_all() 

Dal momento che si sta utilizzando la stessa DB impostato come la vostra applicazione permette per costruire e distruggere un database di test con ogni unit test che esegui.

+1

cosa è 'LimboDBTests' qui? – avichalp

+0

Il nome del progetto era Limbo e io sono apparentemente pessimo a individuare vecchi nomi. – AlexLordThorsen

+2

Perchè 'self.app = Flask (__ name __)' in tearDown() di nuovo? Non penso che sia necessario farlo di nuovo dopo averlo impostato in setUp(). – Houman

4

Riguardo alla risposta di codegeek usando Flask-Testing, ho difficoltà a capire cosa fa createapp(). Flask-SqlAlchemy Introduction into Contexts mi ha fornito un puntatore su come associare in modo dinamico l'oggetto SQLAlchemy all'applicazione. In questo caso, vincolante per l'applicazione di test.

In sostanza:

  1. Creare un oggetto pallone sqlalchemy ma non passano nell'oggetto applicazione
  2. Nella funzione create_app, creare la vostra applicazione di test, e si legano in modo dinamico SQLAlchemy.

tuo myapp.py:

# don't pass in the app object yet 
db = SQLAlchemy() 

def create_test_app(): 
    app = Flask(__name__) 
    app.config['TESTING'] = True 
    app.config["SQLALCHEMY_DATABASE_URI"] = "xxxxxxtestdatabasexxx" 
    # Dynamically bind SQLAlchemy to application 
    db.init_app(app) 
    app.app_context().push() # this does the binding 
    return app 

# you can create another app context here, say for production 
def create_production_app(): 
    app = Flask(__name__) 
    app.config["SQLALCHEMY_DATABASE_URI"] = "xxxxxxproductionxxxx" 
    # Dynamically bind SQLAlchemy to application 
    db.init_app(app) 
    app.app_context().push() 
    return app 

è quindi possibile seguire la soluzione di codegeek come delineato nella Flask-test Documentation

from flask.ext.testing import TestCase 
from myapp import create_app, db 

class MyTest(TestCase): 

    # I removed some config passing here 
    def create_app(self): 
     return create_test_app() 

    def setUp(self): 

     db.create_all() 

    def tearDown(self): 

     db.session.remove() 
     db.drop_all() 

La cosa bella di questa soluzione è che si può crea diverse applicazioni e associa dinamicamente l'oggetto SQLAlchemy utilizzando una funzione. Ogni applicazione può servire a scopi diversi. Ad esempio, uno per la produzione e uno per il test unitario.Nel caso della produzione, puoi chiamare create_production_application() nella tua applicazione di livello superiore.