我正在尝试使用peewee python ORM实现多对多场景,我想要一些单元测试。 Peewee教程很棒,但它假设数据库是在模块级别定义的,然后所有模型都在使用它。我的情况不同:我没有源代码文件(来自python的观点的模块)和我明确运行的测试,我使用的是从该文件收集测试并运行它们的鼻子。
如何仅将自定义数据库用于测试中实例化的模型(由鼻子运行)?我的目标是仅使用内存数据库进行测试,以加快测试过程。
答案 0 :(得分:14)
我今天刚刚提交了一个提交,这使得这更容易。
修复程序采用上下文管理器的形式,允许您覆盖模型的数据库:
from unittest import TestCase
from playhouse.test_utils import test_database
from peewee import *
from my_app.models import User, Tweet
test_db = SqliteDatabase(':memory:')
class TestUsersTweets(TestCase):
def create_test_data(self):
# ... create a bunch of users and tweets
for i in range(10):
User.create(username='user-%d' % i)
def test_timeline(self):
with test_database(test_db, (User, Tweet)):
# This data will be created in `test_db`
self.create_test_data()
# Perform assertions on test data inside ctx manager.
self.assertEqual(Tweet.timeline('user-0') [...])
# once we exit the context manager, we're back to using the normal database
请参阅documentation并查看示例测试用例:
答案 1 :(得分:7)
要在每个测试用例中不包含上下文管理器,请覆盖run
方法。
# imports and db declaration
class TestUsersTweets(TestCase):
def run(self, result=None):
with test_database(test_db, (User, Tweet)):
super(TestUsersTweets, self).run(result)
def test_timeline(self):
self.create_test_data()
self.assertEqual(Tweet.timeline('user-0') [...])
答案 2 :(得分:5)
我从@coleifer和@avalanchy那里得到了很好的答案,然后又向前迈了一步。
为了避免在每个TestCase
子类上覆盖run方法,你可以使用基类...而且我也喜欢不必记下每个模型的想法我上课,所以我想出了这个
import unittest
import inspect
import sys
import peewee
from abc import ABCMeta
from playhouse.test_utils import test_database
from business_logic.models import *
test_db = peewee.SqliteDatabase(':memory:')
class TestCaseWithPeewee(unittest.TestCase):
"""
This abstract class is used to "inject" the test database so that the tests don't use the real sqlite db
"""
__metaclass__ = ABCMeta
def run(self, result=None):
model_classes = [m[1] for m in inspect.getmembers(sys.modules['business_logic.models'], inspect.isclass) if
issubclass(m[1], peewee.Model) and m[1] != peewee.Model]
with test_database(test_db, model_classes):
super(TestCaseWithPeewee, self).run(result)
所以,现在我可以继承TestCaseWithPeewee
而不必担心除了测试之外的任何其他事情
答案 3 :(得分:0)
使用test_database
时,我遇到了test_db
未初始化的问题:
nose.proxy.Exception: Error, database not properly initialized before opening connection
-------------------- >> begin captured logging << --------------------
peewee: DEBUG: ('SELECT "t1"."id", "t1"."name", "t1"."count" FROM "counter" AS t1', [])
--------------------- >> end captured logging << ---------------------
我最终通过传递create_tables=True
来解决这个问题:
def test_timeline(self):
with test_database(test_db, (User, Tweet), create_tables=True):
# This data will be created in `test_db`
self.create_test_data()
根据the docs create_tables
,默认为True
,但最新版本的peewee
似乎并非如此。
答案 4 :(得分:0)
显然,对于上述情况,有一种新方法,您可以在测试用例的setUp()
方法中绑定模型:
the official docs中的示例:
# tests.py
import unittest
from my_app.models import EventLog, Relationship, Tweet, User
MODELS = [User, Tweet, EventLog, Relationship]
# use an in-memory SQLite for tests.
test_db = SqliteDatabase(':memory:')
class BaseTestCase(unittest.TestCase):
def setUp(self):
# Bind model classes to test db. Since we have a complete list of
# all models, we do not need to recursively bind dependencies.
test_db.bind(MODELS, bind_refs=False, bind_backrefs=False)
test_db.connect()
test_db.create_tables(MODELS)
def tearDown(self):
# Not strictly necessary since SQLite in-memory databases only live
# for the duration of the connection, and in the next step we close
# the connection...but a good practice all the same.
test_db.drop_tables(MODELS)
# Close connection to db.
test_db.close()
# If we wanted, we could re-bind the models to their original
# database here. But for tests this is probably not necessary.