自动创建用于测试的数据库
Automating database creation for testing
为了加快测试速度,使用基于内存的 sqlite 会更好,但仍然有必要偶尔使用 MySQL 进行更接近生产的测试。
为了避免枯燥的 discussion/abstract 问题,下面的代码插入了几个词并确认它们在数据库中,对于刚刚提到的两种类型的 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.0, 1.1 的测试示例和教程 (flask/examples/tutorial/tests/conftest.py
) 使用 tempfile.mkstemp()
,这似乎是一个不错的选择。无需担心设置名称和创建数据库,甚至无需关心(随机且可丢弃的)数据库名称。 How/where数据库创建部分完成了吗?
import tempfile
db_fd, db_path = tempfile.mkstemp()
sqlalchemy 似乎不直接支持创建和删除数据库,但 sqlalchemy-utils 支持:https://sqlalchemy-utils.readthedocs.io/en/latest/_modules/sqlalchemy_utils/functions/database.html#create_database.
这将要求您已经有一个 mysql 服务器 运行ning,因此它不允许您使用 tempfile.mkstemp() 来控制文件放置。
另一种方法是 运行 mysql
命令作为自动化测试的一部分。
通过对 manual_set_up
和 test_dbs
的一些修改,我能够 运行 编码。
对于 mysql 数据库,我从 db_url
中删除了数据库名称。而且 db.drop_all()
也失败了,因为数据库不存在所以我输入 try/except 并在此处传递异常。然后在 db.create_all()
之前我创建了一个绕过 db_url
的 sqlachemy 引擎,它没有数据库名称 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()
为了加快测试速度,使用基于内存的 sqlite 会更好,但仍然有必要偶尔使用 MySQL 进行更接近生产的测试。 为了避免枯燥的 discussion/abstract 问题,下面的代码插入了几个词并确认它们在数据库中,对于刚刚提到的两种类型的 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.0, 1.1 的测试示例和教程 (flask/examples/tutorial/tests/conftest.py
) 使用 tempfile.mkstemp()
,这似乎是一个不错的选择。无需担心设置名称和创建数据库,甚至无需关心(随机且可丢弃的)数据库名称。 How/where数据库创建部分完成了吗?
import tempfile
db_fd, db_path = tempfile.mkstemp()
sqlalchemy 似乎不直接支持创建和删除数据库,但 sqlalchemy-utils 支持:https://sqlalchemy-utils.readthedocs.io/en/latest/_modules/sqlalchemy_utils/functions/database.html#create_database.
这将要求您已经有一个 mysql 服务器 运行ning,因此它不允许您使用 tempfile.mkstemp() 来控制文件放置。
另一种方法是 运行 mysql
命令作为自动化测试的一部分。
通过对 manual_set_up
和 test_dbs
的一些修改,我能够 运行 编码。
对于 mysql 数据库,我从 db_url
中删除了数据库名称。而且 db.drop_all()
也失败了,因为数据库不存在所以我输入 try/except 并在此处传递异常。然后在 db.create_all()
之前我创建了一个绕过 db_url
的 sqlachemy 引擎,它没有数据库名称 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()