2015-01-28 13 views
5

Sto testando un'applicazione Flask che ha alcuni modelli SQLAlchemy che usano Flask-SQLAlchemy e sto riscontrando qualche problema nel tentativo di prendere in giro alcuni modelli per alcuni metodi che ricevono alcuni modelli come parametri.Problemi nel tentativo di simulare un modello all'interno di Flask-SQLAlchemy

Una versione giocattolo di quello che sto cercando di fare è così. Supponiamo che io sono un modello dato da:

// file: database.py 
from flask_sqlalchemy import SQLAlchemy 

db = SQLAlchemy() 

class User(db.Model): 
    id = db.Column(db.Integer, primary_key=True) 
    username = db.Column(db.String(80), unique=True) 
    birthday = db.Column(db.Date) 

che viene importato in un app che è costruito con il modello App Factory:

// file: app.py 
from flask import Flask 
from database import db 

def create_app(): 
    app = Flask(__name__) 
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db' 
    db.init_app(app) 

E qualche funzione che ha bisogno di un User come parametro:

// file: actions.py 
import datetime 

SECONDS_IN_A_YEAR = 31556926 

def get_user_age(user): 
    return (datetime.date.today() - user.birthday).total_seconds() // SECONDS_IN_A_YEAR 

Inoltre, nel file app.py e nell'app devono essere presenti un paio di viste e modelli che quest'ultimo chiama la funzione get_user_age da qualche parte.

Il mio problema è: voglio testare la funzione get_user_age senza dover creare un'app, registrarmi con un database falso, ecc. Ecc. Che non dovrebbe essere necessario, la funzione è totalmente indipendente dal fatto che sia usato in un'app Flask.

Così ho provato:

import unittest 

import datetime 
import mock 

from database import User 
from actions import get_user_age 

class TestModels(unittest.TestCase): 
    def test_get_user_age(self): 
     user = mock.create_autospec(User, instance=True) 
     user.birthday = datetime.date(year=1987, month=12, day=1) 
     print get_user_age(user) 

Questo mi solleva un'eccezione RuntimeError: application not registered on db instance and no application bound to current context. Così ho pensato "sì, ovviamente devo correggere alcuni oggetti per impedirgli di controllare se l'app è registrata con il database e così via". Così ho provato a decorarlo con @mock.patch("database.SQLAlchemy") e altre cose senza successo.

Qualcuno sa che cosa dovrei correggere per prevenire questo comportamento o anche se la mia strategia di test è completamente sbagliata?

risposta

6

Quindi, ho trovato una soluzione dopo aver sbattuto la testa sulla tastiera per alcune ore. Il problema sembra essere il seguente (se qualcuno lo sa meglio, correggimi).

Quando eseguo mock.create_autospec(User), il modulo fittizio tenta di esaminare tutti gli attributi di User per creare le specifiche adeguate per l'oggetto Mock che verrà sputato. Quando ciò accade, prova a ispezionare l'attributo User.query, che può essere valutato solo quando si è all'interno dell'ambito di un'app Flask.

Questo accade perché quando viene valutato User.query, viene creato un oggetto che richiede una sessione valida. Questa sessione viene creata dal metodo create_scope_session nella classe SQLAlchemy all'interno di Flask-SQLAlchemy.

Questo metodo crea un'istanza di un classe chiamata SignallingSession cui __init__ metodo chiama il metodo SQLAlchemy.get_app. Questo è il metodo che solleva il RuntimeError quando non c'è nessuna app registrata nel db.

Rattoppando il metodo SignallingSession, tutto funziona correttamente. Dal momento che non voglio interagire con il database questo è ok:

import unittest 
import datetime 

import mock 

from actions import age 


@mock.patch("flask_sqlalchemy.SignallingSession", autospec=True) 
class TestModels(unittest.TestCase): 

    def test_age(self, session): 
     import database 

     user = mock.create_autospec(database.User) 
     user.birthday = datetime.date(year=1987, month=12, day=1) 
     print age(user)