Найти - Пользователи
Полная версия: TestCase и time.sleep
Начало » Python для экспертов » TestCase и time.sleep
1 2 3 4
Андрей Светлов
Kogrom
Я понимаю так, что юнит-тесты и код, который они тестируют сильно взаимосвязаны, так как:
1. Юнит-тесты дают примеры использования кода. То есть получается своеобразная живая документация. Поэтому пользователю-программисту они очень пригодятся. Без них код будет беднее.
2. Юнит-тесты формируют из кода некий недофрейворк (библиотеку, пакет - называйте как хотите). Как они это делают? Проверяют классы на вторичное использование. И это хорошо. Посмотрите на библиотечные функции и классы Python-а - они избыточны для конкретного программиста, но никто особо не страдает. Поэтому не вижу ничего плохого в том, что юнит-тест диктует коду каким ему быть.
Вторичное использование - это правда. Применение тестов позволяет еще раз взглянуть на дизайн системы под новым углом.

Тем не менее я не считаю, что вводить малопонятные крючки, требующиеся только лишь для выполнения тестового кода - хорошая практика.
Если же тесты помогают перепроектировать систему так, чтобы она была не столь монолитна и получила (практически бесплатно) большую гибкость - двумя руками
“за”!

С подходом к тестам как “живой документации” не всё так просто. Изучение чужих библиотек мне подсказывает, что хорошее тестовое покрытие слабо совмещается с ясными и понятными тестами, легко читаемыми сторонним разработчиком.

Для проверки я посмотрел тестовые пакеты для Питона и twisted (с их тестами довольно хорошо знаком) и Пирамиды (новой поделки, имеющей замечательное тестовое покрытие).

По их тестам нельзя изучать систему. Нужно читать исходники и документацию.
Если “что-то пошло не так” - ответ скорее найдется в исходниках, чем в тестах.

Другое дело - работа над изменением библиотеки. Здесь тесты на своём, законном месте. Они замечательно “дают по рукам” и помогают выявить глубокие взаимосвязи, как правило не интересные внешнему разработчику.

Про избыточность библиотечных классов и функций Питона - откровенно говоря, не понял. Как по мне - наоборот. Стандартная библиотека успешно скрывает особенности реализации, давая пользователю простой и понятный интерфейс. Ляпы случаются - но тенденция всё же прослеживается.
Kogrom
Андрей Светлов
Про избыточность библиотечных классов и функций Питона - откровенно говоря, не понял. Как по мне - наоборот. Стандартная библиотека успешно скрывает особенности реализации, давая пользователю простой и понятный интерфейс. Ляпы случаются - но тенденция всё же прослеживается.
Я не про ляпы говорил, не про сложный интерфейс. Взять какой-нибудь модуль, random, например. Имеется куча разнообразных функций, из которых какой-нибудь конкретный программист использует 3-5. Если вести статистику, то может оказаться, что какой-нибудь randrange используют 3 программиста в мире (но и они легко найдут замену). Но никому дела нет - потенциально может быть востребовано. Или есть у разработчиков понятные методы оценки нужности функций в библиотеке?

Возможно, проблема в способе отделения “малопонятного крючка” от параметра, который может быть востребован. Так в примере с def can_show(self, date=None) можно сказать, что тут есть возможность задать дату не нашего компьютера, а удалённого сервера, например. Но неизвестно, будет ли кто-то использовать эту возможность.
Андрей Светлов
Kogrom
проблема в способе отделения “малопонятного крючка” от параметра, который может быть востребован. Так в примере с def can_show(self, date=None) можно сказать, что тут есть возможность задать дату не нашего компьютера, а удалённого сервера, например. Но неизвестно, будет ли кто-то использовать эту возможность.
Если `can_show` и класс `Billing` вообще разрабатываются так, чтобы полностью не зависеть от datetime.now() по дизайну - это хорошо, к такому нужно стремиться. Если вы придумываете новый непротиворечивый сценарий работы, удовлетворяющий тестам и здравому смыслу - почему нет?
А когда каждый метод обрастает несколькими параметрами, необходимыми только для тестов - плохо.

Различие нужно проводить каждый раз заново, смотря на конкретный код.
Kogrom
Всё, до меня дошло :)
Андрей Светлов
Рад за вас. До меня доходило очень долго - и не уверен, что в полной мере осознал :)
Kogrom
Я надеюсь, дошло то, что собеседник хотел сказать про “малопонятные крючки”. Хотя для полной ясности надо бы примеры, но в теории понятно.

До полного понимания TDD и до ясного дизайна мне ещё далеко. В Python ещё приемлемо, а в C++ быстро скатываюсь в какую-то legacy-процедурщину…
Андрей Светлов
Kogrom, я попытаюсь изложить все мои соображения чётко и ясно, создавая по ходу дела нужные примеры.
Не могу обещать, что сделаю это скоро. Но задумка интересная.
Этот топик наткнулся на неочевидную (хоть и распространенную) проблему юнит-тестирования.
Научиться писать self.assertEqual легко, а тестировать живое приложение - гораздо сложнее.
Андрей Светлов
Обещал расписать подробно - вот: http://asvetlov.blogspot.com/2011/02/funny-unittests.html
Kogrom
Андрей Светлов
Обещал расписать подробно - вот: http://asvetlov.blogspot.com/2011/02/funny-unittests.html
Упущен ещё один способ (хотя можно его считать разновидностью третьего):

def _test_html_fresh(self, rst_time, html_time):
return rst_time <= html_time

@property
def is_html_fresh(self):
if not os.path.exists(self.html_path):
return False
rst_time = os.path.getmtime(self.full_path)
html_time = os.path.getmtime(self.html_path)
return self._test_html_fresh(rst_time, html_time)
Фактически при всех подделках мы тестируем вот этот _test_html_fresh. Так почему бы его просто не вынести и не потестировать? Я придумал не очень хорошее имя для этого метода. Возможно, если его подобрать получше, то метод можно было бы сделать даже открытым :)
Андрей Светлов
Как мне кажется, тесты всё же должны работать в первую очередь с public interface.

Сам по себе _test_html_fresh не имеет смысла (по крайней мере в моем случае) - это по сути staticmethod, так как не может вызывать методы экземпляра (потенциально небезопасные). Попробуйте написать аналог для refresh_html.
Приходится полагаться на то, что is_html_fresh не может ошибаться, а все беды только от _test_html_fresh. Таким образом опять отходим от полного покрытия тестами.
Одно дело - когда эта ситуация объясняется недостаточными усилиями программиста.
И совершенно другое - если мы сознательно строим изначально ущербный подход.
К тому же настоятельно не рекомендую использовать третий способ, а вариантов это сделать вопреки всему - миллион.

Подмена FileSystem всяко лучше и удобней.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB