2013-03-27 2 views
6

Я использую Django REST Framework для API, над которым я работаю. По нескольким причинам я хотел бы использовать представления на основе классов. Тем не менее, я немного разбираюсь в своем модульном тестировании, и я никогда не допускаю, чтобы мои модульные тесты касались базы данных. Примечание. Я всегда использую «трюк», продемонстрированный Карлом Майером на Pycon 2012, где он издевается над оболочкой курсора.Стыковка функций в классах на основе Django

cursor_wrapper = Mock() 
cursor_wrapper.side_effect = RuntimeError("No touching the database!") 

@patch('django.db.backends.util.CursorWrapper', cursor_wrapper) 
class TestMyCode(TestCase): 

Здесь link, если вас интересует слайд.

У меня есть метод в одном из представлений, который проверяет что-то в базе данных. Чтобы быть сухим, он распределяется между POST и PUT. Но у меня проблемы с издевательством за мой модульный тест. Это потому, что classmethod as_view создает новую отправку экземпляров и классов и возвращает функцию «обработчик», возвращаемую отправкой. Таким образом, я не могу получить общий метод в своем классе, чтобы издеваться над ним.

Я могу издеваться над моделями, используемыми в представлении класса, но тогда я должен существенно нарушить свою цель быть «СУХОЙ» и скопировать код как в POST, так и в PUT. Думаю, я мог бы реорганизовать код и переместить логику на модель. Но я не уверен, что хочу это сделать.

Как вы можете издеваться над общим методом класса, чтобы избежать фактического касания базы данных? Просто избегайте их?

ответ

3

Я думаю, что вы ответили на свой вопрос. Те же самые вещи, которые вы используете для тестирования любой веб-структуры, применимы к Django, например, инверсия управления и инъекции зависимостей. Вы можете держать его довольно простым в Python, так что не пугайтесь и не отключите его, например, что-то вроде Spring.

Почему бы вам не переместить код из класса? Ваш код по-прежнему не будет сухим, если вам по какой-то причине нужна такая же логика в другом месте. Просто потому, что это Django не означает, что хорошие принципы программирования не применяются.

Я бы предложил просто абстрагировать некоторые вещи в новых классах/модулях python, таких как службы (как понятие, а не определение сервисов Django) и другие логические абстракции для доступа к данным. Затем вы полностью независимы от жизненного цикла запроса/ответа в представлении Django. Тенденция разработчиков Django и Rails заключается в том, чтобы каждый бит логики был непосредственно представлен как в модели, так и в представлении. Это просто приводит к божественным классам и вещам, которые трудно проверить.

Вы также можете облегчить это, если подумать о своем представлении в качестве легкой абстракции, которая обрабатывает такие вещи, как маршаллинг-параметры (GET/POST) и т. Д. Для остальной части вашего кода и вызова необходимой логики, инкапсулированной в другое место. ИМО, если вы хотите тестируемый код, 99% логики должно быть вне контекста сети, если только это не имеет решающего значения для процесса. Это также облегчает работу в фоновом режиме и параллельно.

Что вы должны сделать, это обычные модули и классы python, которые легко проверить, поскольку они не имеют прямых зависимостей от HTTP. Если вам нужно издеваться над HTTP, вы можете просто высмеять объект запроса. Вам повезло, что сочетание python/django позволяет легко выкапывать и издеваться над этими вещами как простые dicts/kwargs.

Одна вещь, которую я поняла, используя представления на основе классов, - это то, что они хороши для использования с mixins и выполнения некоторых соглашений (возврат json, открытие свойств графика и т. Д.), Но с использованием более «расширенных» представлений на основе класса, которые непосредственно требуют моделей такие как DetailView, просто усложняют вещи без необходимости. Эти взгляды хороши для экранов администратора, но для реальных приложений помощь больше, чем больно. Они делают все возможное, чтобы проверить и убить производительность, если вы не найдете хороший, бесшовный способ интеграции слоев кеширования и т. Д. На данный момент, как правило, он просто наследуется от View или TemplateView и выполняется с ним.

Что касается БД, который насмехается конкретно, создайте свои издевательства и пройдите через свою бизнес-логику. Тогда не имеет значения, что вы вводите/выводите, если оно соответствует определенному набору правил и интерфейсов. Например, посмотрите на Mixer. Вы также можете просто создавать/уничтожать временные БД во время тестирования. Один из способов - создать отдельные модули настроек для dev/staging/production/testing и т. Д. И динамически загружать их в зависимости от среды. Таким образом, вы можете избежать повреждения вашей рабочей базы данных разработчиков при выполнении модульных тестов. Конечно, это больше подходит к форме интеграционного тестирования, но вы, вероятно, тоже должны это сделать. Вышеупомянутые решения распространены в других ORM, таких как Hibernate.

В связи с предыдущим вы можете сделать что-то вроде кода ниже в ваших настройках, чтобы использовать базу данных в памяти для модульного тестирования. В конце концов, однако, вы все равно должны рассмотреть тестирование интеграции с вашим фактическим типом хранилища данных, например MySQL

if 'test' in sys.argv: 
    DATABASES['default']['ENGINE'] = 'sqlite3' 

; tldr

  1. Положите ваши взгляды за пределами класса логики в соответствующие объекты и модули.

  2. Не запирайте себя, пытаясь сделать различные связанные с классом взгляды работы для реальных приложений и каждого прецедента; сверните свой собственный.

  3. Использовать в целом хорошие TDD-принципы, такие как IOC, передавая необходимые параметры конструкторам, свободно перемещая вещи, избегая чрезмерных требований к государственному государству (в частности, HTTP).

    1. Избегайте зависимости БД, создавая стандартные макетные объекты (см. № 3) и проходя через служебные интерфейсы (см. № 1).
Смежные вопросы