2017-06-12 1 views
0

J'écris des tests unitaires pour une API REST écrite dans Flask avec l'extension flask_sqlalchemy. Parce que j'ai un certain nombre de classes de modèles, j'ai écrit une sous-classe TestCase pour faire le standard setUp/cleanUp de la base de données de test. Toutes mes classes de test en héritent. Chaque test réussit lorsqu'il est exécuté seul, mais lorsque j'exécute plus d'un test dans une seule classe, le second setUp() échoue sur le self.db.session.commit() (j'essaie d'ajouter une entrée à la table User) car self.db.create_all() a (en silence) échoué pour créer des tables. Voici ma classe de test de base, dans le __init__.py du paquet de test:flask_sqlalchemy create_all() échoue silencieusement dans les tests unitaires

import unittest 

from .test_client import TestClient 
from .. import create_app 
from pdb import set_trace as DBG 

class ApiTest(unittest.TestCase): 
    default_username = 'fred' 
    default_password = 'bloggs' 
    db = None 

    def setUp(self): 
     try: 
      self.app = create_app('testing') 
      self.addCleanup(self.cleanUp) 
      self.ctx = self.app.app_context() 
      self.ctx.push() 
      from .. import db 
      self.db = db 
      self.db.session.commit() 
      self.db.drop_all(app=self.app) 
      from ..models import User, Player, Team, Match, Game 
      # self.app.logger.debug('drop_all())') 
      self.db.create_all(app=self.app) 
      # self.app.logger.debug('create_all())') 
      user = User(user_name=self.default_username) 
      user.password = self.default_password 
      self.db.session.add(u) 
      self.db.session.commit() 
      self.client = TestClient(self.app, user.generate_auth_token(), '') 
     except Exception, ex: 
      self.app.logger.error("Error during setUp: %s" % ex) 
      raise 

    def cleanUp(self): 
     try: 
      self.db.session.commit() 
      self.db.session.remove() 
      self.db.drop_all(app=self.app) 
      # self.app.logger.debug('drop_all())') 
      self.ctx.pop() 
     except Exception, ex: 
      self.app.logger.error("Error during cleanUp: %s" % ex) 
      raise 

Quelqu'un peut-il me dire ce qui ne va pas ici s'il vous plaît?

EDIT: Ajout du code pour create_app() comme demandé.

# chessleague/__init__.py 
import os 

from flask import Flask, g 
from flask_sqlalchemy import SQLAlchemy 
from flask_login import LoginManager 


from . import config 


app = None 
db = None # The database, initialised in create_app() 


def create_app(config_name): 
    app = Flask(__name__) 
    app.config.update(config.get_config(config_name)) 
    # if app.config['USE_TOKEN_AUTH']: 
    #  from api.token import token as token_blueprint 
    #  app.register_blueprint(token_blueprint, url_prefix='/auth') 
    import logging 
    from logging.handlers import SysLogHandler 
    syslog_handler = SysLogHandler() 
    syslog_handler.setLevel(logging.WARNING) 
    app.logger.addHandler(syslog_handler) 

    login_manager = LoginManager() 
    login_manager.login_view = 'auth.login' 
    login_manager.init_app(app) 
    global db 
    db = SQLAlchemy(app) 
    db.init_app(app) 
    from .models import User,Player,Game,Match,Team,Post 
    db.create_all() 
    from .api import api as api_blueprint 
    app.register_blueprint(api_blueprint, url_prefix='/chessleague') 
    return app 

`

+0

Vous avez parlé d'un problème avec le 'create_all' mais n'ont pas ajouté la façon dont elle est invoquée. Je suppose que ça s'appelle 'create_app ', pouvez-vous ajouter cette fonction à votre question? – Fian

+0

'db.create_all()' est appelé dans la méthode 'setUp()' ci-dessus, mais votre question m'a amené à regarder 'create_app()' dans le paquet principal. Il s'avère qu'il appelle aussi 'db.create_all()', comme vous l'avez deviné. Je l'ai ajouté à la question. – ProfCalculus

+0

... mais AFAIK 'create_all()' devrait être idempotent. – ProfCalculus

Répondre

0

create_all() s'applique aux métadonnées qui sont découvertes en important des modules avec des modèles. Dans votre cas, les métadonnées des modèles se lient au db de votre models.py mais vous appelez la fonction create_all() de chessleague/__init__.db de create_app(), qui est un objet différent pour SqlAlchemy. Vous pouvez résoudre ce problème en utilisant db de models.py:

from .models import User,Player,Game,Match,Team,Post, db as models_db 
models_db.create_all() 
0

Voici le séquence d'initialisation qui a fonctionné pour moi - commentaires de bienvenue!

Ma classe de test setUp() appelle create_app(config_name) à partir du package de l'application principale. Le principal paquet d'applications (__init__.py) crée l'instance d'application au niveau du module, à savoir app=Flask(my_app_package_name) Ensuite ma fonction create_app(config_name)

  • charge la configuration à droite dans app.config (y compris le droit SQLACHEMY_DATABASE_URL)
  • importe le modèle classes et db (comme model_db) de models.py

Cette importation crée le symbole db au niveau du module dans models.py, fol lowed par les définitions de classe modèle:

# models.py 
    from . import app 
    db = SQLAlchemy(app) 
    ... 
    class User(db.Model) 
    ... 
    etc 

Maintenant, tout est mis en place correctement: le symbole « db » peuvent être importés de partout models.py, et je peux appeler db.create_all() avec succès de mon test setUp().

@Fian, pouvez-vous poster votre solution en réponse afin que je puisse vous donner un crédit?