Para pruebas más rápidas, es mejor usar un sqlite basado en memoria, pero de vez en cuando es necesario usar MySQL para pruebas que coincidan más con la producción. Para evitar una discusión seca/pregunta abstracta, el siguiente código inserta un par de palabras y confirma que están en la base de datos, para ambos tipos de bases de datos SQL que acabamos de mencionar.
from flask import Flask from flask_sqlalchemy import SQLAlchemy import unittest db = SQLAlchemy() TEST_DATABASE_URL_MEMORY = 'sqlite:///:memory:' TEST_DATABASE_URL_MYSQL = 'mysql+pymysql://root:@127.0.0.1:3306/somewords' def create_app(db_url): app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = db_url app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db.init_app(app) return app class Word(db.Model): __tablename__ = 'words' id = db.Column(db.Integer, primary_key=True, autoincrement=True) word = db.Column(db.String(32), index=True) class TestInsertion(unittest.TestCase): def manual_set_up(self, db_url): self.app = create_app(db_url) self.app_context = self.app.app_context() self.app_context.push() db.drop_all() db.create_all() def insert(self): words = ['hello', 'world'] for word in words: w = Word(word=word) db.session.add(w) db.session.commit() for word in words: assert Word.query.filter_by(word=word).first() is not None def test_dbs(self): for db_url in [TEST_DATABASE_URL_MEMORY, TEST_DATABASE_URL_MYSQL]: self.manual_set_up(db_url) self.insert()
El primero (sqlite) pasa. El segundo falla:
E sqlalchemy.exc.InternalError: (pymysql.err.InternalError) (1049, "Unknown database 'somewords'")
Podemos, cada vez que ejecutamos las pruebas, crear la base de datos
> mysql -u root -p Welcome to the MySQL monitor. Commands end with ; or \g. mysql> create database somewords; Query OK, 1 row affected (0.02 sec) mysql> quit;
pero esto excluye las pruebas automatizadas, por lo que supongo que me estoy perdiendo algo básico.
¿Cómo se puede ejecutar una prueba como la anterior, automatizando la creación de la base de datos?
Actualizar
Los ejemplos de prueba para 1.0 , 1.1 y el tutorial ( flask/examples/tutorial/tests/conftest.py
) usan tempfile.mkstemp()
, que parece una buena manera de hacerlo. No es necesario preocuparse por establecer el nombre y crear la base de datos, ni siquiera preocuparse por el nombre de la base de datos (aleatorio y descartable). ¿Cómo/dónde se realiza la parte de creación de la base de datos?
import tempfile db_fd, db_path = tempfile.mkstemp()
La creación y eliminación de bases de datos no parece ser compatible directamente con sqlalchemy, pero es compatible con sqlalchemy-utils: https://sqlalchemy-utils.readthedocs.io/en/latest/_modules/sqlalchemy_utils/functions/database.html# crear_base_de_datos .
Esto requerirá que ya tenga un servidor mysql ejecutándose, por lo que no le permitirá controlar la ubicación del archivo con tempfile.mkstemp().
Otro enfoque sería ejecutar el comando mysql
como parte de la prueba automatizada.
Con algunas modificaciones en manual_set_up
y test_dbs
pude ejecutar el código.
Para la base de datos mysql, elimino el nombre de la base de datos de db_url
. Y db.drop_all()
también falla porque la base de datos no existe, así que introduzco try/except y paso la excepción aquí. Luego, antes db.create_all()
, creo un motor sqlachemy sin pasar por db_url
que no tiene el nombre de base de datos db.create_engine(db_url)
.
# your imports ... import sqlalchemy.exc #... TEST_DATABASE_URL_MYSQL = 'mysql+pymysql://root:@127.0.0.1:3306/' def manual_set_up(self, db_url, db_kind, db_name=None): if db_kind == "mysql": self.app = create_app(db_url + db_name) else: self.app = create_app(db_url) self.app_context = self.app.app_context() self.app_context.push() try: db.drop_all() except sqlalchemy.exc.InternalError as e: if "unknown database" in str(e.args[0]).lower(): pass try: db.create_all() except sqlalchemy.exc.InternalError as e: if "unknown database" in str(e.args[0]).lower(): db.create_engine(db_url, {}).execute(f"CREATE DATABASE IF NOT EXISTS {db_name};") db.create_all() def test_dbs(self): for args in [(TEST_DATABASE_URL_MEMORY, "sqlite"), (TEST_DATABASE_URL_MYSQL, "mysql", "somewords")]: self.manual_set_up(*args) self.insert()