Блог django на хабрахабре
Django Framework / Замена fixtures для тестов или обзор factory-boy
- 16 Янв 15:41
В Django есть такая удобная вещь для написания тестов — это fixtures. Удобство состоит в том, что ваши тесты могут входить в уже заполненный данными проект. Например тестируем работу админчасти статистики, надо иметь готовый массив данных, с которым оперируем и проверяем результаты. Неудобство состоит в том, что эти фикстуры надо где взять, надо поддерживать актуальными, такими-же актуальными как и тесты. Вот как раз и про неудобную часть, а также паре подводных камней я бы и хотел вам рассказать.
Рассказывать, что это означает я не буду, но то есть хорошая документация по фикстурам у самой Django .
Я «обплетал» тесты уже готового написанного проекта. В нагрузку с проектом идет дамп базы, которая, как это не удивительно, может быть не целостная. Самый часты бок — это когда записи по форенключу нет. Например у Вас есть профиль, но нет юзера или есть транзакция между не существующими счетами.
Самое обидное, что Джанго Вам не поможет решить эту проблему. И получите что-то типа
Error: Unable to serialize database:
Нагугил тикет в Django Code:
https://code.djangoproject.com/ticket/6773
К которому прилагается команда, которая показывает Вам «разбитые модели», т. е. модели не полные с неверными данными в ForeignKey .
Я ее немного приукрасил возможностью удалять их автоматом https://gist.github.com/1018947. Для реальных данных удаление автоматом — это не очень обдуманный шаг, но мне сейчас надо получить хоть какую-то фикстуру.
Для поддержки актуальности базы между всеми разработчиками используется django-south, мне кажется это уже давно стало стандартом Django разработки. Тот же механизм можно использовать для поддержки актуальности с фикстурами, поэтому я одну фикстуру полностью перегоняю в sqlite3 базу, которую как и фикстуру держу в репозитарии проекта и для доступа к которой использую отдельный сетингс.
Сеттингс файл для этого состоит из 3х строчек (settings_lights.py):
Как известно, в любую команду можно передать не стандартное имя сетингс модуля.
Например, для того, чтоб запустить его и добавить новых данных:
А поддерживать актуальность фикстуры можно через миграции, которые вы создаете после изменения структуры базы
Для тестирования я использую тот-же settings_lights.py для того, чтобы использовать sqlite3 в тестах, при этом для тестов вся база будет держаться в памяти, что существенно ускорит процесс написания тестов и тестирования их.
Но я думаю как финальную проверку, после того, как вы закончили с разработкой ( доработкой ) тестов можно использовать и реальный Engine.
А собственно сам текст тестов может выглядить так:
Этот пример ничего не тестирует, а просто показывает Вам, что данные на момент запуска тестов в базе уже есть. Фикстуры можно хранить как в папке fixtures любой апы, не только тестируемой. А еще в сетингсах можно прописать:
Про сигналы в Django вы можете почитать в документции.
Фикстура — это по сути сериализация ОРМ объектов, т. е. объект будет сохранен как json, как просто текст. А значит загрузка из фикстуры — это поочередное добавление всех объектов, а добавление объектов связано с вызовом сигналов, которые в свою очередь могу сами создавать объекты моделей или изменять существующие.
Например. У Вас есть 2 модели счета и транзакции. При добавлении транзакции — дергается сигнал, по которому изменяются балансы счетов участников этой транзакции. При подготовке фикстуры вы создали одну транзакцию между двумя счетами на сумму 100 рублей, т. е. после ее проведения на одном счету прибавится 100 рублей, а на другой вычтится. Вы сохраните полученные данные в файл фикстуры, в которой будут готовые записи со счетами и транзакциями. Во время тестирования этот файл будет загружаться и вначале загрузятся модели счетов – на одном 100, на другом -100. После загрузятся транзакции и дернут сигнал, который еще раз изменит балансы на счетах и мы во время тестирования увидим состояния на счетах 200 и -200.
Решение у джанги есть , но почему-то не документированное, и как по мне — очень не удачное.
В обработчик сигнала передается параметр raw который True во время загрузки фиксутры.
Так что, если вы не хотите, чтоб обработчик сигнала работал в момент загрузки фикстуры, то первые 3 строчки вашего обработчика могут выглядит так:
Как по мне — это недокументированную возможность надо огромными буквами задокументировать в обоих разделах — тесты и сигналы, но я думаю будут решения и лучше этой проблемы.
У меня все. Я описал то, как с фикстурами работаю я, и очевидно, что они могут сэкономить очень много времени Вам при разработке тестов, а также могут помогать Вам делать более качественные и реальные тесты.
Хотелось бы в комментариях увидеть критику такого подхода, дополнения, подводные камни, с которым вы сталкиваетесь. Буду дополнять статью Вашими цитатами и идеями.
Спасибо, и удачных Вам выходных.
нужно передать первичный ключ объекта ContentType для модели Product, для этого применяем свой тег (### Image
-
model: shop.image
pk: 1
fields:
src: static/shop/test/product/borsh.jpg
description: Наваристый супец!
content_type: !!python/object/apply:resty.utils.content_type_pk [shop, product]
object_id: 1
content_type: !!python/object/apply:resty.utils.content_type_pk [shop, product]), который вызывает простую функцию: def content_type_pk(app_label, model):
return ContentType.objects.get(app_label=app_label, model=model).pk
попробуйте сделать такое в json или xml не получится =)