Pull to refresh

Comments 47

раз, два, три

То-ли ссылки не вставились, то ли ещё что. А какие есть, а то я кроме django-any и его форка больше ничего не знаю?
поправили ссылки, спасибо. Я знаю хорошие отзывы про django-dynamic-fixture :)
Перед каждым запуском теста база не создается. Она создается один раз, потом перед каждым тестом говорится start transaction, а после tearDown() говорится rollback;
при каждом запуске ./manage.py test — база создается :)

между тестами да обычно транзакции, но может быть и flush — в статье это опоминалось…
> Для ускорения тестовой базы можно еще поставить в postgresql.conf:
> Прирост тоже ощущается. Ну и SSD винчестеры тоже хорошо :).

Просто инициализируйте тестовый PG-кластер в /dev/shm
Кстати,

>Такие тесты проще включить в процесс сборки, они достаточно быстро проходят (3-4 минуты ~250 тестов) и не задерживают особо релиз, они рядом с кодом. За временем выполнения тестов нужно следить и принимать меры по ускорению, т.к. количество тестов будет только расти, а значит — и время их выполнения.

Вполне логично в таких случаях сразу ориентироваться на распределённое тестирование. Для py.test существуют простые плагины, которые реализуют такую возможность. Для nose довольно легко написать похожий плагин, взяв за основу pytest-xdist.
Спасибо за инфрмацию.

Про pytest вкурсе, даже про распределенность его читал, но как-то все больше именно nose становиться в качестве основного пускальщика.

Нужно еще посмотреть повнимательнее.
По изоляции мне нравятся больше интеграционные тесты, по тестируемому объекту — функциональные. У таких тестов очень большое покрытие кода, это и плюс и минус одновременно… они достаточно быстро проходят (3-4 минуты ~250 тестов)
как будто про нас. :) тоже с этого начинали. и щастье при первом рефакторинге. и разочарование, когда более-менее освоили метод, а количество тестов перевалило за сотню — запуск занимает какое-то неразумное время, при этом видно невооруженным глазом, что получаемая польза не оправдывает вложения.

чем крупнее кусок системы, охватываемый тестом, тем больше у него параметров состояния и тем меньше шансов проверить, пускай даже лишь существенные, их комбинации, ничего не упустив. при этом, чем больше глубина куска (по задействованным уровням системы), тем менее очевидны связи между этими параметрами, с точки зрения внешнего наблюдателя, коим является тест.

мне кажется, что «большое покрытие кода» интеграционными тестами — иллюзия. как раз о коде они дают лишь крайне поверхностное, оценочное представление. по сто раз дергают одни и те же куски, пропуская значимые граничные аспекты. отсюда эффективность. данный вид тестов хоть и хорош в начале, но в качестве основного средства tdd-разработки не особо практичен. вроде как хождение на четвереньках.
Это вопрос о гранулярности тестов. Потому что

> чем крупнее кусок системы, охватываемый тестом, тем больше у него параметров состояния

>по сто раз дергают одни и те же куски

— это те самые симптомы, о которых начинают упоминать в случаях неправильной гранулярности.
у меня опыт интенсивного TDD в python уже пару лет точно — это осознанный выбор, с учетом опыта…

Да покрытие кода не говорит про качество тестов, но я описал плюс покрытия кода для питона.

Нужно писать хорошие тесты, как и нужно писать хороший код :).

Если поверх WSGI трудно тестировать какую-то логику, нужно спускаться ниже, и ниже уже проверять тонкую логику (например: на джанго форме или моделе) — это уже ближе к юнит тестам…

Тест поверх WSGI все равно нужен — он проверит код в реальном воркфлоу :), пусть даже не со всей логикой.
на случай, если кто будет тестить

без fsync = off база создаетс адски долго. А с ним — реально секунды
Я так понял- автор выставил код уже с привязкой к совему проекту, а не реюзабельное решение
в приципе там особо привязок нет, я немного обновил gist, добавил requirements.txt и пару коментов. У нас это пакет, поэтому нужно его создать… Но для публикации идеи мне казалось gist-a достаточно…
psql( 'create extension cube;' 'create extension earthdistance;' )

Про редис в статье ни слова

def mock_http(self): self.mock_func('urllib2.urlopen') self.mock_func('urllib2.build_opener') self.mock_func('requests.api.request')

Вот это зачем?
1. setup_databases пишем под себя, написал об этом в шапке раннера

2. да про редис забыл :), для зачистки обвертка…

3. mock_http — тесты не должны сутчатся на внешний http, т.к. он может отвалиться или сети вообще не быть, а тесты должны ходить в не зависимости, отвечает сайт или нет. Поэтому мы мокаем http запросы. mock_http нужен, чтоб видеть что тест делает http и свалиться, в трейсбеке можно увидеть, где возник этот запрос — потом идем мокаем :)… так же можно запустить с ключем --without-mock-http :)

ок, согласен gist заточен под проект :)
Просто у меня в голове крутилась именно идея того, что в репе лежит sql ый дамп постгри и его используют вместо фикстур.

И в текстах он указывается также, как и фикстуры. Так можно даже организовывать поддержку транимых процедур или тригеров.

sql — дамп нужно также поддерживать, как и статические фикстуры, что тут другого?

В setup_databases(это из gist) можно все что угодно сделать с базой, в том числе и подтянуть sql дамп с диска, или прям строкой записать в базу тригер — это не важно.

Потом между тестами мы ее будем создавать с WITH TEMPLATE (мы ведь про postgres) и там у нас будут уже все тригиры и данные, которые мы туда запихнем на шаге создания бд… Эта операция (создание бд с WITH TEMPLATE) быстрее чем flush на большой базе, но медленее транзакций. Про это в статье говориться.
Выигрыш будет не только между тестами но и в первом тесте. А при TDD это реально выигрыш во времени. Нет?

Не вижу сложностей в поддержке sql дампа в репе. Саус накатывает миграции. Все инсерты идут один за другим в отиличии от фикстур, поэтому легко резолвить конфликты в дампах. Помоему сказка.
у нас есть отдельно данные для теста, там дамп мама не горюй :)

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

у нас большая достаточно команда, гит и куча бранчей, тестовая база — дамп на 5 гиг :)… Все не так-то просто :)
5ти гиговая база для тестов? Мы же сейчас не про стресс тестирование говорим?

А как вы ее поддерживаете? По сути тоже самое, только без дампа. Под каждый бранч отдельная 5ти гиговая дура висит :)
Ремарка: для ручного тестирования :)

там данные, которые похожи на прод и их там много, на них проверяем миграции…

Нет не под каждый бранч :), одна на все бренчи — но это отдельная история :)
а без django_nose можно это реализовать наследником от обыкновенного DjangoTestSuiteRunner?
если без django_nose и nose так понимаю, то наследник DjangoTestSuiteRunner + свой TestCase со всей логикой из плагинов
да я ща кручусь — похоже на то, что да. Надо еще TestCase переписывать, хотя пробую один хук еще заюзать. Может получится
можно вместо TestCase — свой DataBaseWraper для тестов.
А в каких случаях вы используете флаг _test_unique_db а в каких нет?
У нас был момент, когда мы использовали только уникальную базу, а когда подключали транзакции, то часть тестов не работали в транзакции и почему было не очевидно :)… Поэтому те которые не работали оставили с уникальной базой. В общем пока точно не знаю, предстоит разобраться… :)
Вот и мне тоже стало интересно :) Может внутри кода создавалось еще одно соединение с базой?

Но мне кажется разобраться стоит, т.к. на транзакциях работает в 10 раз быстрее чем с клонированием.

Незабудте сообщить, как выясните :)
а свойство _test_db — нужно на случай использования различных Баз?
да, в setup_databeses я сохраняю несколько шаблонов баз, по этому атрибуту получаю какую базу нужно клонировать для конкретного теста.
а можешь про вот это объяснить по подробнее?
* изолированность от внешнего мира (внешние http запросы должны мокаться);

Я не очень понял.
тут 3 пункт habrahabr.ru/company/ostrovok/blog/146552/#comment_4936729, вроде ответил :)

у нас много внешних зависимостей: шлюзы оплаты, провайдеры, просто какие-то внешние сервисы с апи… это все нужно мокать — мы мокаем… И для этого обвертка mock_http, чтоб разработчики понимали, что тест стучится во внешний мир…
Да, прости, этот ответ не увидел. Спасибо
gist.github.com/3017754 небольшая вариация на тему :)

За основу — взял Ваш. Правда убрал пару, как мне показалось, не нужных вещей.

Можете глянуть? Что я могу упустить?
сразу что в глаза бросается, это если у нескольких людей одинаковый TEST_DB_PREFIX

TEST_DB_PREFIX = settings.DATABASES['default']['NAME'] + '__'

то так нельзя запустить тесты нескольким пользователям с базой на одном серваке… т.к. все базы у первого попытаются грохнуть Runner второго…

Ну и зря убрали REUSE_DB, я про это в статье писал, запуск одного отдельного теста должен быть 1-2 секунды, а не даже 10-20сек, а создание базы может занять и больше времени…
Если тип БД не критичен, то при разработке для тестов можно использовать sqlite3 и в качестве имени БД ':memory:' — тесты выполняются на порядок быстрее.
> Про быстрый SQLite в памяти можно забыть, в проекте есть привязки к особенностям postgres, да и идентичность тестового окружения все-таки важна, поэтому тесты тоже работают на postgres.

:) критичен
Один из признаков псевдо-TDD — необходимость «поддерживать тесты, чтобы не стали мертвым грузом»:
В TDD тесты правятся первыми не потому, что их обязательно надо поддерживать, а потому, что они определяют требования/спецификации: изменились бизнес-требования (или другой код) -> изменить формальные спецификации (а это и есть тесты) -> изменить сам код так, чтобы он удовлетворял новым спецификациям.

Ключевая разница между написанием любых автоматизированных тестов и именно TDD — в голове: тесты воспринимаются не как груз для проверки кода, который еще почему-то нужно писать перед самим кодом, а как естественный этап проектирования: от спецификаций на естественном языке переходим к спецификациям в программном коде (вместо или в дополнение к рисованию на бумажке), которые и помогают написать правильный код.
Согласен. Поэтому статья и называется — внедряем TDD :).
fsync=off это почти тоже что рам. Я пробовал и чисто рам, но что-то прироста от него было мало, пробовал правда давненько.
«найти сломанное место иногда трудно»
Не согласен, использую TDD около 3ёх лет и за редким исключением проблемы находятся мгновенно.
в функциональных тестах с большим покрытием кода? в каждом куске кода, который такой тест покрывает потенциально может быть ошибка, если код знаешь с нуля, то да все найдется быстро, если в проект пришел через код его существования, то все тонкости не будешь знать…

с юнит тестами да все просто — но на то они и юнит…
Sign up to leave a comment.