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

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

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


person Community    schedule 13.04.2013    source источник
comment
Мне это тоже интересно. PyORMish может справиться с этим, установив значение db_config в модели перед запуском теста, а затем установив его обратно в метод разборки. Я ожидаю, что это будет работать и с Peewee.   -  person Aaron Meier    schedule 14.04.2013


Ответы (5)


Я только что нажал коммит сегодня, который делает это проще.

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

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

См. документацию и ознакомьтесь с примеры тестов:

person coleifer    schedule 14.04.2013
comment
Это все еще путь, когда дело доходит до тестирования? - person Christopher Shroba; 24.12.2016

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

Я взял отличные ответы от @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 и мне не нужно беспокоиться ни о чем другом, кроме теста

person g3rv4    schedule 17.09.2014

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

person amorphic    schedule 26.10.2015
comment
Трассировка указывает, что тестовая база данных находится в отложенном состоянии. Тестовая база данных должна быть инициализирована именем базы данных. - person coleifer; 29.10.2015

Судя по всему, для описанного сценария есть новый подход, когда вы можете связать модели в методе 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.

person Jonathan    schedule 10.10.2020