web-dev-qa-db-fra.com

Comment puis-je tester une application Flask qui utilise SQLAlchemy?

J'ai une application Web fonctionnelle sur Flask avec SqlAlchemy pour la modération des actualités, elle dispose de quelques méthodes api pour gérer les demandes de modération, comme approuver, refuser les actualités actuellement sélectionnées, les lister, etc.

Je veux écrire des tests unitaires sur ces méthodes, et je les ai fait fonctionner, mais je ne comprends pas comment implémenter l'exécution de toutes les requêtes que je fais à partir des cas de test dans une session db, afin de pouvoir supprimer toutes les modifications apportées à la base de données. Ou existe-t-il un autre moyen plus propre ou approprié de le faire?

J'ai découvert que peut-être tout ce dont j'avais besoin était "scoped_session" dans SqlAlchemy, mais toutes mes tentatives pour l'implémenter ont échoué. Si c'est le cas, dites-moi où utiliser ces lignes de code (dans les paramètres ou dans la méthode set_up du scénario de test).

from sqlalchemy.orm import scoped_session
from sqlalchemy.orm import sessionmaker
session_factory = sessionmaker()
Session = scoped_session(session_factory) 
24
Ellochka Cannibal

Je vous suggère d'utiliser l'extension Flask-Testing . Il s'agit d'une extension approuvée qui vous permet d'effectuer les tests unitaires comme vous le souhaitez. Il a également une section spécifique pour SQLAlchemy.

Test avec SQLAlchemy

Cela couvre quelques points si vous utilisez Flask-Testing avec SQLAlchemy. Il est supposé que vous utilisez l'extension Flask-SQLAlchemy, mais sinon, les exemples ne devraient pas être trop difficiles à adapter à votre propre configuration particulière.

Tout d'abord, assurez-vous de définir l'URI de la base de données sur autre chose que votre base de données de production ! Deuxièmement, c'est généralement une bonne idée de créer et de supprimer vos tables à chaque exécution de test, pour garantir des tests propres: "

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()
21
codegeek

C'est ainsi que j'ai récemment effectué des tests unitaires. Je suppose que puisque vous utilisez SQLAlchemy que vous utilisez des classes de modèle. Je suppose également que toutes vos tables sont définies en tant que classes de modèle 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()

Puisque vous utilisez la même configuration de base de données que votre application, cela vous permet de créer et de détruire une base de données de test avec chaque test unitaire que vous exécutez.

15
AlexLordThorsen

En ce qui concerne la réponse de codegeek utilisant Flask-Testing, j'ai du mal à comprendre ce que fait createapp(). Flask-SqlAlchemy Introduction in Contexts m'a fourni un pointeur sur la façon de lier dynamiquement un objet SQLAlchemy à votre application. Dans ce cas, liaison à l'application de test.

Fondamentalement:

  1. Créez un flask sqlalchemy object mais ne passez pas dans l'objet app
  2. Dans la fonction create_app, créez votre application de test et liez dynamiquement SQLAlchemy.

Votre 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

Vous pouvez ensuite suivre la solution de codegeek comme indiqué dans la documentation de Flask-Test

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 bonne chose à propos de cette solution est que vous pouvez créer différentes applications et lier dynamiquement l'objet SQLAlchemy à l'aide d'une fonction. Chaque application peut avoir des objectifs différents. Par exemple, un pour la production et un pour le test unitaire. Dans le cas de la production, vous pouvez appeler create_production_application () dans votre application de niveau supérieur flask.

10
user2659443