10

我正在尝试使用 peewee python ORM 实现多对多场景,我想要一些单元测试。Peewee 教程很棒,但它假设数据库是在模块级别定义的,然后所有模型都在使用它。我的情况不同:我没有明确运行的测试的源代码文件(从python的角度来看的模块),我正在使用从该文件收集测试并运行它们的nose。

如何仅将自定义数据库用于在测试中实例化的模型(由鼻子运行)?我的目标是仅使用内存数据库进行测试,以加快测试过程。

4

6 回答 6

13

我今天刚刚推送了一个提交,这使得这更容易。

该修复采用上下文管理器的形式,它允许您覆盖模型的数据库:

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

请参阅文档并查看示例测试用例:

于 2013-04-14T16:25:34.307 回答
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') [...])
于 2014-04-01T14:35:13.877 回答
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,而不必担心除了测试之外的任何其他事情

于 2014-09-17T15:37:51.360 回答
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()

根据文档 create_tables应该默认为,True但在最新版本的peewee.

于 2015-10-26T13:17:40.800 回答
0

显然,对于所描述的场景有一种新方法,您可以在setUp()测试用例的方法中绑定模型:

来自官方文档的示例:

# 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.

于 2020-10-10T17:50:02.820 回答
0

对于使用 pytest 的任何人,我都是这样做的:

conftest.py

MODELS = [User, Tweet]  # Also add get_through_model() for ManyToMany fields

test_db = SqliteDatabase(':memory:')
test_db.bind(MODELS, bind_refs=False, bind_backrefs=False)
test_db.connect()
test_db.create_tables(MODELS)

@pytest.fixture(autouse=True)
def in_mem_db(mocker):
    mocked_db = mocker.patch("database.db", autospec=True)  # "database.db" is where your app's code imports db from
    mocked_db.return_value = test_db
    return mocked_db

,您的所有测试都使用内存中的 sqlite 数据库运行。

于 2022-03-04T20:20:36.433 回答