Пользовательская база данных sqlite для модульных тестов кода с использованием peewee ORM

Я пытаюсь реализовать сценарий "многие ко многим", используя peewee python ORM, и мне нужны некоторые модульные тесты. Учебник Peewee велик, но он предполагает, что база данных определена на уровне модуля, то все модели используют его. Моя ситуация другая: у меня нет файла исходного кода (модуля с точки зрения python) с тестами, которые я запускаю явно, я использую нос, который собирает тесты из этого файла и запускает их.

Как использовать пользовательскую базу данных только для моделей инстанцируется в тестах (которые запускаются носом)? Моя цель-использовать базу данных в памяти только для тестов, чтобы ускорить процесс тестирования.

4 ответов


Я просто нажал фиксацию сегодня, что делает это проще.

исправление выполнено в виде контекстного менеджера, который позволяет переопределить базу данных модели:

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

посмотреть документация и посмотрите на примеры тестов:


чтобы не включать context manager в каждый тестовый случай, перезапишите 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') [...])

Я взял отличные ответы от @coleifer и @avalanchy и сделал их еще один шаг вперед.

чтобы избежать переопределения метода run на каждом TestCase подкласс, вы можете использовать базовый класс... и мне также нравится идея не записывать класс модели, с которым я работаю, поэтому я придумал это

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 и не нужно беспокоиться ни о чем, кроме теста


при использовании 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.