Я пишу модуль Python, и я хотел бы к модульному тесту его. Я плохо знаком с Python и несколько обманутый доступными опциями.
В настоящее время я хотел бы записать свои тесты как doctests, как мне нравится декларативный, а не обязательный стиль (однако, не стесняйтесь разуверять меня этого предпочтения, если это дезинформировано). Это поднимает несколько вопросов, однако:
не стесняйтесь разуверить меня в этом предпочтение, если оно дезинформирует
Я считаю, что использовал doctest
более широко (намного расширяя границы его использования по назначению), чем любой другой разработчик открытого кода, по крайней мере в рамках одного проекта - все тесты в моем gmpy проекте являются doctests. Это было совершенно новым в то время, когда gmpy
только начинался, это казалось отличным маленьким трюком, и если что-то стоит делать, то стоит делать это в избытке - верно?)
Ошибаетесь. За исключением gmpy
, где переделывать всё под правильные юнит-тесты было бы слишком накладно, я больше никогда не допускал этой ошибки: в наши дни я использую юнит-тесты как юнит-тесты, а доктесты просто для проверки документации, как они всегда и должны были использоваться. То, что делают доктесты (сравнивают ожидаемый и фактический результат на равенство - и все), просто не является хорошей или надежной основой для создания надежного набора тестов. Иначе и быть не могло.
Я бы рекомендовал вам посмотреть на nose. Модуль unittest
в новом Python 2.7 намного богаче и приятнее, и если вы застряли на 2.4, 2.5 или 2.6, вы все равно можете использовать новые возможности с помощью unittest2, который вы можете скачать и установить; nose
хорошо дополняет unittest
.
Если вы терпеть не можете unittest (но - попробуйте, он вам понравится!), попробуйте py.test, альтернативный пакет с совершенно другой философией.
Но, пожалуйста, не растягивайте doctest
для тестирования чего-либо, кроме примеров в документации! Сравнение "точно-качественно" слишком часто встает на вашем пути, как мне пришлось узнать за свой (метафорический;-) счет в gmpy
...
Мне не нравятся доктесты по следующим причинам:
Этот список взят из моего сообщения в блоге Что мне не нравится в doctest , где есть еще кое-что и длинная ветка комментариев, в которых обсуждаются моменты.
О покрытии: я не верю, что есть инструмент покрытия для Python, который измерял бы охват в рамках тестов. Но поскольку это просто длинные списки операторов без ветвей или циклов, это проблема?
doctests отлично подходят для быстрых, второстепенных модульных тестов, которые описывают некоторые из основных способов использования рассматриваемых объектов (поскольку они отображаются в строках документации и, следовательно, помогают (что угодно) и т. Д.) .
Я лично обнаружил, что обширное и более тщательное тестирование более эффективно с использованием модуля unittest, и теперь модуль 2.7 (обратно перенесенный на unittest2) имеет еще более удобные утверждения. Вы можете настроить наборы тестов и любой сложный сценарий с помощью фреймворка модульного тестирования и покрыть целые ряды различных тестов за один раз (из командной строки)
покрытия.py , от Неда Батчелдера и как Упоминания @bstpierre будут работать с любым из них, и я рекомендую его, чтобы увидеть, что вы тестировали в коде, а что нет. Вы можете добавить его в систему CI (например, Hudson или что-то еще, что вы хотите использовать), чтобы не отставать от того, что покрыто, а что нет, а отчеты HTML отлично подходят для того, чтобы увидеть, что не было затронуто тестированием. Покрытие поддерживает вывод Junit xml, который многие системы CI знают, как отображать текущие результаты в диаграммах, чтобы вы могли видеть, как со временем сборка становится лучше или хуже.
У меня есть подозрение, что Алекс может быть немного впереди меня на кривой программиста, но если вы хотите услышать мнение человека с некоторым опытом работы с Python (в качестве "пользователя", а не эксперта или евангелиста), но не в той же лиге, мои выводы о тестировании модулей были практически такими же.
Doctests может показаться отличным вариантом для простого тестирования в начале, и я пошел в этом направлении для какого-то личного проекта дома, потому что это было рекомендовано в другом месте. На работе мы используем nose (хотя он настолько консервирован и завёрнут, что у меня сложилось впечатление, что до недавнего времени мы использовали pyUnit), и несколько месяцев назад я перешёл на nose и дома.
Первоначальное время на установку и управление, а также отделение от реального кода, может показаться ненужным в начале, особенно когда вы тестируете что-то не очень большое, но в долгосрочной перспективе я обнаружил, что доктесты встают на пути каждого рефакторинга или реструктуризации, которые я хотел сделать, их довольно трудно поддерживать, практически невозможно масштабировать, и это очень быстро компенсирует первоначальную экономию. И да, я знаю, что модульное тестирование - это не то же самое, что интеграционное тестирование, но доктесты имеют тенденцию слишком строго определять ваши единицы для вас. Они также не очень хорошо подходят для unit-based agile, если вы когда-нибудь решите, что это подходящий инструмент для набросков или модель разработки.
Возможно, вам потребуется немного времени, чтобы спланировать и затем усовершенствовать свои модульные тесты так, как это делает pyUnit или nose, но есть шанс, что даже в краткосрочной перспективе вы обнаружите, что это действительно помогает вам на многих уровнях. Я знаю, что это помогло мне, и я относительно недавно столкнулся со сложностью и масштабом кодовой базы, над которой я работаю в настоящее время. Просто нужно сжать зубы на первые несколько недель.
Чтобы узнать о покрытии, посмотрите отличный extension.py .
В остальном все, что написал Алекс Мартелли, очень точно.
Я согласен со всеми вышеприведенными пунктами о том, что doctest не масштабируется, и я предпочитаю придерживаться unittest.
Один совет, который я могу внести, это вызывать юнит-тесты из обработки кода __name__ == "__main__
, так что если файл тестов запускается как скрипт, он будет запускать свои тесты.
eg:
#!/usr/bin/env python
"""
Unit tests for the GetFiles.py utility
"""
import unittest
from FileUtilities import getTree
class TestFileUtilities(unittest.TestCase):
def testGetTree(self):
"""
Tests that a known tree is found and incidentally confirms
that we have the tree we expected to use for our current
sample extraction.
"""
found = getTree('./anzmeta-dtd', '.pen')
expected_path_tail = ['ISOdia.pen',
'ISOgrk1.pen',
'ISOtech.pen']
for i, full_path in enumerate(found):
assert full_path.endswith( expected_path_tail[i] ), expected_path_tail[i]
# other tests elided
if __name__ == "__main__":
# When this module is executed from the command-line, run all its tests
unittest.main()