Куда идут модульные тесты Python?

Если вы пишете библиотеку или приложение, куда деваются файлы модульного теста?

Приятно отделить тестовые файлы от основного кода приложения, но неудобно помещать их в подкаталог «tests» внутри корневого каталога приложения, поскольку это затрудняет импорт модулей, которые вы будете тестировать.

Есть ли здесь лучшая практика?

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
511
0
95 113
18

Ответы 18

Я не верю, что существует устоявшаяся «лучшая практика».

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

Затем я добавляю основной каталог приложения в sys.path The problem is if your tests contain some helper modules (like test web server) and you need to import such helper modules from within your proper tests.
Piotr Dobrogost 29.11.2011 23:21

Вот как это выглядит для меня: os.sys.path.append(os.dirname('..'))

Mattwmaster58 09.07.2018 07:25

Распространенной практикой является размещение каталога тестов в том же родительском каталоге, что и ваш модуль / пакет. Итак, если бы ваш модуль назывался foo.py, макет вашего каталога выглядел бы так:

parent_dir/
  foo.py
  tests/

Конечно, нет единого способа сделать это. Вы также можете создать подкаталог tests и импортировать модуль с помощью абсолютный импорт.

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

Я бы хотел это сделать, но не могу заставить это работать. Чтобы запустить тесты, я нахожусь в parent_dir и набираю: "python tests \ foo_test.py", а в foo_test.py: "из ..foo импортируйте это, это, другое" Это не удается с: "ValueError: Attempted относительный импорт без пакета "И parent_dir, и тесты содержат в этом.py, поэтому я не уверен, почему они не являются пакетами. Я подозреваю, что это потому, что сценарий верхнего уровня, который вы запускаете из командной строки, не может считаться (частью) пакета, даже если он находится в каталоге с в этом.py. Итак, как мне запустить тесты? Я сегодня работаю над Windows, благослови мои хлопковые носки.

Jonathan Hartley 07.05.2009 20:35

Лучший способ - я нашел - запускать модульные тесты - это установить вашу библиотеку / программу, а затем запускать модульные тесты с носом. Я бы порекомендовал virtualenv и virtualenvwrapper, чтобы сделать это намного проще.

Cristian 16.05.2009 13:27

@Tartley - вам нужен файл в этом.py в вашем каталоге «tests» для работы импорта Absolution. У меня есть этот метод для работы с носом, поэтому я не уверен, почему у вас проблемы.

cmcginty 27.06.2009 05:42

Спасибо, Кейси, но у меня есть файл в этом.py во всех соответствующих каталогах. Я не знаю, что я делаю не так, но у меня есть эта проблема во всех моих проектах Python, и я не могу понять, почему никто другой не делает. О боже.

Jonathan Hartley 22.07.2009 12:23

Одно из решений моей проблемы с Python2.6 или новее: запускать тесты из корня вашего проекта, используя: python -m project.package.tests.module_tests (вместо python project / package / tests / module_tests.py) . Это помещает каталог тестового модуля в путь, поэтому тесты могут затем выполнить относительный импорт в свой родительский каталог, чтобы получить тестируемый модуль.

Jonathan Hartley 22.07.2009 12:25

nose и nose2 находятся в режиме обслуживания. С веб-сайта nose: Nose находится в режиме обслуживания в течение последних нескольких лет и, вероятно, прекратит работу без нового человека / команды, которые возьмут на себя поддержку. В новых проектах следует рассмотреть возможность использования Nose2, py.test или просто unittest / unittest2.

alpha_989 28.01.2018 22:59

из nose2 github (github.com/nose-devs/nose2): Несмотря на то, что плагины unittest2 так и не появились, нос2 все еще поддерживается! У нас есть небольшое сообщество, заинтересованное в продолжении работы и использования носа2. Однако, учитывая текущую ситуацию, когда к pytest проявляется гораздо больший интерес, ноу2 уделяет приоритетное внимание исправлениям ошибок и обслуживанию перед разработкой новых функций.

alpha_989 28.01.2018 23:00

Так что, если вы еще не используете nose / nose2, возможно, лучше использовать py.test.

alpha_989 28.01.2018 23:00

Я недавно начал программировать на Python, так что у меня еще не было возможности узнать о лучших практиках. Но я написал модуль, который находит все тесты и запускает их.

Так что я:

app/
 appfile.py
test/
 appfileTest.py

Мне нужно будет посмотреть, как это будет происходить, когда я перейду к более крупным проектам.

Привет, CamelCase - это немного странно в мире питонов.

Stuart Axon 05.10.2015 22:15

Я использую каталог tests/, а затем импортирую основные модули приложения, используя относительный импорт. Итак, в MyApp / tests / foo.py может быть:

from .. import foo

для импорта модуля MyApp.foo.

"ValueError: Попытка относительного импорта не в пакете"

cprn 25.07.2016 22:12

В C# я обычно выделяю тесты в отдельную сборку.

В Python - до сих пор - я имел тенденцию либо писать тесты, где тест находится в строке документации функции, либо помещать их в блок if __name__ == "__main__" в нижней части модуля.

Для файла module.py модульный тест обычно должен называться test_module.py в соответствии с соглашениями об именах Pythonic.

Есть несколько общепринятых мест для размещения test_module.py:

  1. В том же каталоге, что и module.py.
  2. В ../tests/test_module.py (на том же уровне, что и каталог кода).
  3. В tests/test_module.py (один уровень под каталогом кода).

Я предпочитаю №1 из-за простоты поиска тестов и их импорта. Какую бы систему сборки вы ни использовали, ее можно легко настроить для запуска файлов, начиная с test_. Собственно, файл шаблон unittest по умолчанию, используемый для обнаружения тестов - test*.py.

протокол load_tests по умолчанию ищет файлы с именем test * .py. Кроме того, этот лучший результат Google и эта документация unittest используют формат test_module.py.

dfarrell07 14.08.2013 01:24

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

juniper- 26.08.2013 21:10

@juniper, следуя вашим мыслям, module_test будет отображаться в автозавершении, когда вы пишете код. Это может сбивать с толку или раздражать.

Medeiros 11.10.2013 08:21

При развертывании кода мы не хотим развертывать тесты в наших производственных местах. Таким образом, размещение их в одном каталоге './tests/test_blah.py' легко изменить при развертывании. ТАКЖЕ, некоторые тесты берут образцы файлов данных, и наличие их в тестовом каталоге жизненно важно, чтобы мы не развертывали тестовые данные.

Kevin J. Rice 04.05.2015 17:10

@ KevinJ.Rice Разве вы не должны проверять, работает ли код на ваших производственных площадках?

endolith 18.06.2017 18:00

@endolith, Не работает тестовый код (функциональное тестирование) на prod box, только запускает тесты на dev и QA и, возможно, промежуточные ящики, если они у нас есть. Ящики продуктов заблокированы и имеют доступ к производственным базам данных и т. д., А тесты могут случайно получить доступ к этой критически важной инфраструктуре. Лучше не включать тесты в продукты, если вы не пытаетесь сделать что-то очень конкретное, например, службы мониторинга, в этом случае мы говорим о мониторинге, а не о модульных тестах.

Kevin J. Rice 12.08.2017 06:00

Предполагается ли, что путь к модулю уже был добавлен в Python, чтобы вы могли импортировать модуль в свои файлы тестирования?

Ken T 24.08.2017 13:04

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

anarcat 15.09.2017 16:12

При написании пакета под названием «foo» я помещу модульные тесты в отдельный пакет «foo_test». Модули и подпакеты будут иметь то же имя, что и модуль пакета SUT. Например. тесты для модуля foo.x.y находятся в foo_test.x.y. Файлы __init__.py каждого пакета тестирования затем содержат набор AllTests, который включает все наборы тестов пакета. setuptools предоставляет удобный способ указать основной пакет тестирования, так что после «python setup.py develop» вы можете просто использовать «python setup.py test» или «python setup.py test -s foo_test.x.SomeTestSuite» для просто конкретный сюит.

Если тесты простые, просто поместите их в строку документации - большинство тестовых фреймворков для Python смогут это использовать:

>>> import module
>>> module.method('test')
'testresult'

Для других более сложных тестов я бы поместил их либо в ../tests/test_module.py, либо в tests/test_module.py.

Я также стараюсь помещать свои модульные тесты в сам файл, как отмечает Джереми Кантрелл выше, хотя я стараюсь не помещать тестовую функцию в основное тело, а скорее помещаю все в

if __name__ == '__main__':
   do tests...

блокировать. В итоге в файл добавляется документация в виде «примера кода» для использования тестируемого файла python.

Должен добавить, что я обычно пишу очень плотные модули / классы. Если для ваших модулей требуется очень большое количество тестов, вы можете поместить их в другой, но даже в этом случае я бы все равно добавил:

if __name__ == '__main__':
   import tests.thisModule
   tests.thisModule.runtests

Это позволяет любому, кто читает ваш исходный код, знать, где искать тестовый код.

"как отмечает Джереми Кантрелл" где?

endolith 13.08.2014 19:46

Мне нравится такой способ работы. Самый простой из редакторов может быть настроен для запуска вашего файла с помощью горячей клавиши, поэтому, когда вы просматриваете код, вы можете мгновенно запускать тесты. К сожалению, на языках, отличных от Python, это может выглядеть ужасно, поэтому, если вы работаете в магазине C++ или Java, ваши коллеги могут это осудить. Это также плохо работает с инструментами покрытия кода.

Keeely 15.06.2017 12:57

Мы используем

app/src/code.py
app/testing/code_test.py 
app/docs/..

В каждый тестовый файл мы вставляем ../src/ в sys.path. Это не самое лучшее решение, но оно работает. Я думаю, было бы здорово, если бы кто-то придумал что-то вроде maven в java, которое дает вам стандартные соглашения, которые просто работают, независимо от того, над каким проектом вы работаете.

Я предпочитаю каталог тестов верхнего уровня. Это означает, что импорт становится немного сложнее. Для этого у меня есть два решения:

  1. Используйте setuptools. Затем вы можете передать test_suite='tests.runalltests.suite' в setup() и просто запустить тесты: python setup.py test
  2. Установите PYTHONPATH при запуске тестов: PYTHONPATH=. python tests/runalltests.py

Вот как это поддерживается кодом в M2Crypto:

Если вы предпочитаете проводить тесты с помощью носовых тестов, возможно, вам нужно сделать что-то другое.

Тот же вопрос возник у нас при написании Pythoscope (https://pypi.org/project/pythoscope/), который генерирует модульные тесты для программ Python. Перед тем, как выбрать каталог, мы опросили людей о тестировании в списке Python, было много разных мнений. В конце концов, мы решили поместить каталог «tests» в тот же каталог, что и исходный код. В этом каталоге мы генерируем тестовый файл для каждого модуля в родительском каталоге.

Потрясающие! Просто запустил Pythoscope (отличный логотип) на каком-то устаревшем коде, и это было потрясающе. Мгновенные передовые методы, и вы можете заставить других разработчиков заполнить уже неуспешные тестовые заглушки. А теперь подключить его к бамбуку? :)

Will 02.04.2012 00:44

Как я это делаю ...

Структура папки:

project/
    src/
        code.py
    tests/
    setup.py

Setup.py указывает на src / как расположение, содержащее модули моих проектов, затем я запускаю:

setup.py develop

Что добавляет мой проект в пакеты сайтов, указывая на мою рабочую копию. Для запуска своих тестов я использую:

setup.py tests

Используя тот тестовый раннер, который я настроил.

Кажется, вы отказались от термина «модуль». Большинство программистов Python, вероятно, подумают, что модуль - это файл, который вы назвали code.py. Было бы разумнее назвать каталог верхнего уровня «проектом».

blokeley 05.04.2011 15:50

Исходя из моего опыта разработки фреймворков тестирования на Python, я бы посоветовал поместить модульные тесты Python в отдельный каталог. Поддерживайте симметричную структуру каталогов. Это было бы полезно для упаковки только основных библиотек, а не для упаковки модульных тестов. Ниже реализована принципиальная схема.

                              <Main Package>
                               /          \
                              /            \
                            lib           tests
                            /                \
             [module1.py, module2.py,  [ut_module1.py, ut_module2.py,
              module3.py  module4.py,   ut_module3.py, ut_module.py]
              __init__.py]

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

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

Jasha 01.06.2018 14:41

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

Jasha 01.06.2018 14:43

Всего 1 тестовый файл

Если имеется только 1 тестовый файл, рекомендуется поместить его в каталог верхнего уровня:

module/
    lib/
        __init__.py
        module.py
    test.py

Запустить тест в CLI

python test.py

Множество тестовых файлов

Если у вас много тестовых файлов, поместите его в папку tests:

module/
    lib/
        __init__.py
        module.py
    tests/
        test_module.py
        test_module_function.py
# test_module.py

import unittest
from lib import module

class TestModule(unittest.TestCase):
    def test_module(self):
        pass

if __name__ == '__main__':
    unittest.main()

Запустить тест в CLI

# In top-level /module/ folder
python -m tests.test_module
python -m tests.test_module_function

Используйте unittest discovery

unittest discovery найдет все тесты в папке пакета.

Создайте __init__.py в папке tests/

module/
    lib/
        __init__.py
        module.py
    tests/
        __init__.py
        test_module.py
        test_module_function.py

Запустить тест в CLI

# In top-level /module/ folder

# -s, --start-directory (default current directory)
# -p, --pattern (default test*.py)

python -m unittest discover

Ссылка

  • pytest Передовой опыт для тестового макета
  • unittest

Фреймворк модульного тестирования

Я рекомендую вам проверить некоторые основные проекты Python на GitHub и почерпнуть некоторые идеи.

Когда ваш код становится больше и вы добавляете больше библиотек, лучше создать тестовую папку в том же каталоге, что и у вас setup.py, и отразить структуру каталогов вашего проекта для каждого типа теста (unittest, интеграция, ...)

Например, если у вас есть такая структура каталогов:

myPackage/
    myapp/
       moduleA/
          __init__.py
          module_A.py
       moduleB/
          __init__.py
          module_B.py
setup.py

После добавления тестовой папки у вас будет такая структура каталогов, как:

myPackage/
    myapp/
       moduleA/
          __init__.py
          module_A.py
       moduleB/
          __init__.py
          module_B.py
test/
   unit/
      myapp/
         moduleA/
            module_A_test.py
         moduleB/
            module_B_test.py
   integration/
          myapp/
             moduleA/
                module_A_test.py
             moduleB/
                module_B_test.py
setup.py

Многие правильно написанные пакеты Python используют ту же структуру. Очень хороший пример - пакет Boto. Проверить https://github.com/boto/boto

Рекомендуется использовать «тесты» вместо «test», потому что «test» - это встроенный модуль Python. docs.python.org/2/library/test.html

brodul 14.12.2017 12:59

Не всегда .. например matplotlib имеет его под matplotlib/lib/matplotlib/tests (github.com/matplotlib/matplotlib/tree/…), sklearn имеет его под scikitelearn/sklearn/tests (github.com/scikit-learn/scikit-learn/tree/master/sklearn/te‌ sts)

alpha_989 28.01.2018 22:52

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

Основная причина для этого: рефакторинг.

Когда я что-то перемещаю, я действительно хочу, чтобы тестовые модули двигались вместе с кодом; легко потерять тесты, если они находятся в отдельном дереве. Давайте будем честными, рано или поздно вы получите совершенно другую структуру папок, такую ​​как джанго, фляга и многие другие. Что нормально, если тебе все равно.

Главный вопрос, который вы должны себе задать:

Я пишу:

  • а) многоразовая библиотека или
  • б) создание проекта, чем объединение нескольких полураздельных модулей?

Если:

Отдельная папка и дополнительные усилия по поддержанию ее структуры могут подойти лучше. Никто не будет жаловаться на то, что ваши тесты получат развернут в производство.

Но также легко исключить тесты из распространения, если они смешаны с основными папками; поместите это в setup.py:

find_packages("src", exclude=["*.tests", "*.tests.*", "tests.*", "tests"]) 

Если b:

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

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

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

в чем вообще проблема с развертыванием тестов в продакшн? по моему опыту, это очень полезно: позволяет пользователям фактически запускать тесты в своей среде ... когда вы получаете отчеты об ошибках, вы можете попросить пользователя запустить набор тестов, чтобы убедиться, что там все в порядке, и отправить горячие патчи для теста suite напрямую ... кроме того, буду не из-за того, что вы помещаете свои тесты в module.tests, попадает в производство, если вы не сделали что-то не так в своем установочном файле ...

anarcat 15.09.2017 16:09

Как я уже писал в своем ответе, я обычно не разделяю тесты. Но. Включение тестов в дистрибутив может привести к выполнению вещей, которые обычно не нужны в производственной среде (например, некоторый код уровня модуля). Это, конечно, зависит от того, как написаны тесты, но на всякий случай, если вы их оставите, никто не запустит что-то вредоносное по ошибке. Я не против включения тестов в дистрибутивы, но понимаю, что, как правило, это безопаснее. А размещение их в отдельной папке позволяет очень легко не включать их в dist.

Janusz Skonieczny 30.10.2017 10:19

Я помещаю свои тесты в тот же каталог, что и тестируемый код (CUT); для foo.py тесты будут проводиться в foo_ut.py или аналогичном. (Я настраиваю процесс обнаружения тестов, чтобы найти их.)

Это помещает тесты рядом с кодом в список каталогов, делая очевидным, что тесты есть, и делает открытие тестов настолько простым, насколько это возможно, когда они находятся в отдельном файле. (Для редакторов командной строки, vim foo* и при использовании графического браузера файловой системы просто щелкните файл CUT, а затем непосредственно соседний тестовый файл.)

Как и другие указали, это также упрощает рефакторинг и извлечение кода для использования в другом месте, если это когда-либо понадобится.

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

Другие вопросы по теме