自定义sqlite数据库,用于使用peewee ORM进行代码的单元测试

时间:2023-01-20 22:24:17

I am trying to implement a many-to-many scenario using peewee python ORM and I'd like some unit tests. Peewee tutorial is great but it assumes that database is defined at module level then all models are using it. My situation is different: I don't have a source code file (a module from python's point of view) with tests which I run explicitly, I am using nose which collects tests from that file and runs them.

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

How do I use a custom database only for models instantiated in tests (which are being run by nose)? My goal is to use an in-memory database for tests only, to speedup the testing process.

如何只对在测试中实例化的模型(由nose运行)使用自定义数据库?我的目标是仅对测试使用内存中的数据库,以加速测试过程。

4 个解决方案

#1


12  

I just pushed a commit today that makes this easier.

我今天只是推动了一个承诺,让它变得更容易。

The fix is in the form of a context manager which allows you to override the database of a model:

修复以上下文管理器的形式进行,允许您覆盖模型的数据库:

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

See the documentation and have a look at the example testcases:

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

#2


7  

To not include context manager in every test case, overwrite run method.

若要在每个测试用例中不包含上下文管理器,请覆盖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') [...])

#3


5  

I took the great answers from @coleifer and @avalanchy and took them one step further.

我从@coleifer和@avalanchy那里得到了很好的答案,并将它们向前推进了一步。

In order to avoid overriding the run method on every TestCase subclass, you can use a base class... and I also like the idea of not having to write down every model class I work with, so I came up with this

为了避免在每个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)

so, now I can just inherit from TestCaseWithPeewee and don't have to worry about anything else other than the test

因此,现在我可以从TestCaseWithPeewee继承并不必担心除测试之外的其他内容

#4


0  

When using test_database I encountered problems with test_db not being initialized:

在使用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 << ---------------------

nose.proxy。例外:错误,数据库没有正确初始化之前打开连接- - - - - - - - - - - - - - - - - - - - - - > >开始捕获日志< < - - - - - - - - - - - - - - - - - - - - - -小东西:调试:('选择“t1”。”id”、“t1”。“名称”、“t1”。“”从“计数器”t1”,[])- - - - - - - - - - - - - - - - - - - - - - - - > >结束捕获日志< < - - - - - - - - - - - - - - - - - - - - - - - -

I eventually fixed this by passing create_tables=True like so:

我最终通过像这样传递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()

def test_timeline(self):使用test_database(test_db, (User, Tweet), create_tables=True): #这个数据将在' test_db ' self.create_test_test_test_data()中创建

According to the docs create_tables should default to True but it seems that isn't the case in the latest release of peewee.

根据文档create_tables应该默认为True,但在最近发布的peewee中似乎并不是这样。

#1


12  

I just pushed a commit today that makes this easier.

我今天只是推动了一个承诺,让它变得更容易。

The fix is in the form of a context manager which allows you to override the database of a model:

修复以上下文管理器的形式进行,允许您覆盖模型的数据库:

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

See the documentation and have a look at the example testcases:

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

#2


7  

To not include context manager in every test case, overwrite run method.

若要在每个测试用例中不包含上下文管理器,请覆盖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') [...])

#3


5  

I took the great answers from @coleifer and @avalanchy and took them one step further.

我从@coleifer和@avalanchy那里得到了很好的答案,并将它们向前推进了一步。

In order to avoid overriding the run method on every TestCase subclass, you can use a base class... and I also like the idea of not having to write down every model class I work with, so I came up with this

为了避免在每个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)

so, now I can just inherit from TestCaseWithPeewee and don't have to worry about anything else other than the test

因此,现在我可以从TestCaseWithPeewee继承并不必担心除测试之外的其他内容

#4


0  

When using test_database I encountered problems with test_db not being initialized:

在使用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 << ---------------------

nose.proxy。例外:错误,数据库没有正确初始化之前打开连接- - - - - - - - - - - - - - - - - - - - - - > >开始捕获日志< < - - - - - - - - - - - - - - - - - - - - - -小东西:调试:('选择“t1”。”id”、“t1”。“名称”、“t1”。“”从“计数器”t1”,[])- - - - - - - - - - - - - - - - - - - - - - - - > >结束捕获日志< < - - - - - - - - - - - - - - - - - - - - - - - -

I eventually fixed this by passing create_tables=True like so:

我最终通过像这样传递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()

def test_timeline(self):使用test_database(test_db, (User, Tweet), create_tables=True): #这个数据将在' test_db ' self.create_test_test_test_data()中创建

According to the docs create_tables should default to True but it seems that isn't the case in the latest release of peewee.

根据文档create_tables应该默认为True,但在最近发布的peewee中似乎并不是这样。