自动创建数据库以进行测试

Cal*_*laf 6 python sqlite sqlalchemy flask pymysql

为了更快地进行测试,最好使用基于内存的sqlite,但仍需要偶尔使用MySQL进行测试,使其更接近于生产环境。为了避免枯燥的讨论/抽象问题,对于上面提到的两种类型的SQL数据库,下面的代码都会插入几个单词并确认它们已在数据库中。

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()
Run Code Online (Sandbox Code Playgroud)

第一个(sqlite)通过。第二个失败:

E       sqlalchemy.exc.InternalError: (pymysql.err.InternalError) (1049, "Unknown database 'somewords'")
Run Code Online (Sandbox Code Playgroud)

每次运行测试,我们都可以创建数据库

> 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;
Run Code Online (Sandbox Code Playgroud)

但这排除了自动化测试的可能,因此我猜想我缺少一些基本知识。

通过自动创建数据库,如何自动执行上述测试?

更新资料

测试实例为1.01.1,和教程(flask/examples/tutorial/tests/conftest.py)的使用tempfile.mkstemp(),这似乎是一个很好的路要走。无需担心设置名称和创建数据库,也不必担心(随机且可丢弃的)数据库名称。数据库创建部分如何/在哪里完成?

import tempfile
db_fd, db_path = tempfile.mkstemp()
Run Code Online (Sandbox Code Playgroud)

met*_*irr 3

经过一些修改,manual_set_uptest_dbs能够运行代码。

对于 mysql 数据库,我从db_url. 并且db.drop_all()由于数据库不存在而失败,因此我放入了 try/ except 并在此处传递异常。然后在我创建一个绕过没有数据库名称的 db.create_all()sqlachemy 引擎之前。db_urldb.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()
Run Code Online (Sandbox Code Playgroud)