Пользовательская база данных 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
.