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

时间:2019-07-15 21:48:55

标签: python sqlite flask sqlalchemy 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()

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

E       sqlalchemy.exc.InternalError: (pymysql.err.InternalError) (1049, "Unknown database 'somewords'")

我们可以在每次运行测试时创建数据库

> 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;

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

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

更新

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

import tempfile
db_fd, db_path = tempfile.mkstemp()

2 个答案:

答案 0 :(得分:3)

通过对manual_set_uptest_dbs进行一些修改,我可以运行代码。

对于mysql数据库,我从db_url中删除了数据库名称。并且db.drop_all()也会失败,因为该数据库不存在,所以我放入了try / except并在此处传递异常。然后在db.create_all()之前,我绕过db_url创建了一个没有数据库名称db.create_engine(db_url)的sqlachemy引擎。

# 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()

答案 1 :(得分:1)

sqlalchemy似乎不直接支持创建和删除数据库,但是sqlalchemy-utils:https://sqlalchemy-utils.readthedocs.io/en/latest/_modules/sqlalchemy_utils/functions/database.html#create_database支持它。

这将需要您已经在运行mysql服务器,因此它不允许您使用tempfile.mkstemp()控制文件的位置。

另一种方法是在自动化测试中运行mysql命令。