ПУТЬ проблема с pytest 'ImportError: нет модуля с именем YadaYadaYada'

231

Я использовал easy_install для установки pytest на Mac и начал писать тесты для проекта с файловой структурой, например:

repo/
repo/app.py
repo/settings.py
repo/models.py
repo/tests/
repo/tests/test_app.py

бегать py.test в каталоге репо, все ведет себя так, как вы ожидаете

но когда я пытаюсь сделать то же самое в Linux или Windows (оба имеют pytest 2.2.3 на них), он лает всякий раз, когда ему удается выполнить первый импорт чего-либо из пути моего приложения. Скажем напримерfrom app import some_def_in_app

Нужно ли редактировать мой PATH для запуска py.test на этих системах? Кто-нибудь испытывал это?

MattoTodd
источник
4
Вот способ исправить это с помощью setuptools.
ederag
4
Пожалуйста, проверьте ответ @hoefling и подумайте над тем, чтобы изменить принятый ответ, если SO позволяет после этого долго: намного лучше!
Давиде

Ответы:

91

Да, исходная папка не находится в пути Python, если вы находитесь cdв каталоге tests.

У вас есть 2 варианта:

  1. Добавьте путь вручную к тестовым файлам, примерно так:

    import sys, os
    myPath = os.path.dirname(os.path.abspath(__file__))
    sys.path.insert(0, myPath + '/../')
  2. Запустите тесты с помощью env var PYTHONPATH=../.

Not_a_Golfer
источник
11
когда я иду cdв каталог? Я бегу py.testот своего корня. если я не ошибаюсь, и вы имеете в виду, как pytest проходит через мои папки
MattoTodd
если бы это было cdпроблемой, разве я не ударил бы это и на Mac?
MattoTodd
О, я неправильно прочитал и подумал, что это не работает из каталога тестов. все же трюк в предложении 1 сработает. Я использую только Linux, поэтому не могу объяснить поведение на других ОС.
Not_a_Golfer
у вас есть такой импорт для всех ваших файлов test.py?
MattoTodd
4
да, но моя структура каталогов обычно немного отличается - я обычно держу / src и / test в корневом каталоге.
Not_a_Golfer
276

Я не уверен, почему py.test не добавляет текущий каталог в сам PYTHONPATH, но вот обходной путь (который должен выполняться из корня вашего репозитория):

python -m pytest tests/

Это работает, потому что Python добавляет текущий каталог в PYTHONPATH для вас.

киви
источник
2
Требуется переписать относительный импорт в абсолютный, если у вас есть код для запуска приложения не на том уровне, с которого вы выполняете команду. Например: project/test/all-my-testsи project/src/app.pyиз-за этого изменения нужно вызвать app.pyкосвенно, используя __main__.pyфайл project/src, чтобы можно было использовать вызов python -m src. Насколько я могу судить, довольно грязные вещи.
Зельфир Кальцталь
3
@Zelphir: использование абсолютного импорта является рекомендуемой практикой. У Habnabit's есть хорошая статья о лучших методах упаковки: blog.habnab.it/blog/2013/07/21/python-packages-and-you , и PEP8 говорит, что «неявный относительный импорт никогда не должен использоваться и был удален в Python 3.» Смотрите: python.org/dev/peps/pep-0008 .
Apteryx
1
@Apteryx Вы имеете в виду "проект-абсолют", верно? Потому что такие вещи /home/user/dev/projectxyz/src ...были бы очень плохими и не работали на других машинах в большинстве случаев. Я думаю, что я имел в виду, что я должен всегда записывать весь корень проекта в путь к модулю, даже если модуль находится в той же папке, что и файл. Я не знал, что это считается лучшей практикой, так что это полезная информация, спасибо. Я согласен с большинством pep8, хотя это все еще не идеально.
Зельфир Кальцталь
1
@ Зельфир, да, это то, что я имел в виду. Я считаю, что термин абсолютный импорт в Python всегда относится к «абсолютному проекту». Смотрите: python.org/dev/peps/pep-0328/#rationale-for-absolute-imports . На самом деле, я уверен, что вы не можете импортировать из случайных, абсолютных путей, по крайней мере, используя механизм «импорта» по умолчанию.
Apteryx
4
Я добавил __init__.pyв тестах, что решил проблему. Теперь я могу использоватьpytest
Киран Кумар Котари
154

conftest решение

Наименее инвазивным решением является добавление пустого файла с именем conftest.pyв repo/каталоге:

$ touch repo/conftest.py

Вот и все. Нет необходимости писать собственный код для искажения sys.pathили не забудьте перетащить PYTHONPATHили разместить__init__.py в каталог, где он не принадлежит.

Каталог проекта впоследствии:

repo
├── conftest.py
├── app.py
├── settings.py
├── models.py
└── tests
     └── test_app.py

объяснение

pytestвнешний вид для conftestмодулей на тестовой коллекции , чтобы собрать пользовательские крючки и крепления, а также для того , чтобы импортировать пользовательские объекты из них, pytestдобавляет родительский каталог conftest.pyкsys.path (в данном случаеrepo каталога).

Другие структуры проекта

Если у вас есть другая структура проекта, поместите conftest.pyв корневой каталог пакета (тот, который содержит пакеты, но не сам пакет, поэтому не содержит __init__.py), например:

repo
├── conftest.py
├── spam
   ├── __init__.py
   ├── bacon.py
   └── egg.py
├── eggs
   ├── __init__.py
   └── sausage.py
└── tests
     ├── test_bacon.py
     └── test_egg.py

src расположение

Хотя этот подход можно использовать с srcмакетом (место conftest.pyв srcдиректории):

repo
├── src
   ├── conftest.py
   ├── spam
      ├── __init__.py
      ├── bacon.py
      └── egg.py
   └── eggs 
       ├── __init__.py
       └── sausage.py
└── tests
     ├── test_bacon.py
     └── test_egg.py

Имейте в виду, что добавление srcк PYTHONPATHсмягчает значение и преимущества srcмакета! Вы закончите тестированием кода из репозитория, а не установленного пакета. Если вам нужно сделать это, возможно, вам вообще не нужен srcкаталог.

Куда пойти отсюда

Конечно, conftestмодули - это не просто файлы, помогающие в обнаружении исходного кода; это то место, где происходят все специфичные для проекта улучшения pytestинфраструктуры и настройки вашего набора тестов. pytestимеет много информации о conftestмодулях, разбросанных по всем документам ; начать с conftest.py: локальные плагины для каждого каталога

Кроме того, у SO есть отличный вопрос по conftestмодулям: что такое использование файлов conftest.py в py.test?

Хефлинг
источник
2
@ aaa90210, хотя я не могу воспроизвести вашу проблему (импорт из conftest в корневом каталоге работает на любом уровне), вы никогда не должны импортировать из conftest файлов, так как это зарезервированное имя, pytestи настоятельно рекомендуется не делать этого. Делая это, вы сажаете семена для будущих ошибок. Создайте другой модуль с именем utils.pyи поместите туда код для повторного использования в тестах.
переманивание
4
Недурно! Это единственное решение, которое хорошо работает для меня.
130
4
обязательно должен быть принят ответ - спасибо!
Мартин Пек
2
По логике conftest.pyне относится к коду приложения, и, следовательно, размещение его под src/неправильным кодом .
Ник О'Лай
2
Этот ответ должен быть фиксированным заголовком на SO. Большое спасибо.
Ригоберта Равиолини
119

У меня такая же проблема. Я исправил это, добавив пустой __init__.pyфайл в мой testsкаталог.

Арон Керзон
источник
77
Обратите внимание, что это не рекомендуется py.test: avoid “__init__.py” files in your test directories. This way your tests can run easily against an installed version of mypkg, independently from the installed package if it contains the tests or not.SRC: pytest.org/latest/goodpractises.html
K.-Michael Aye
24
Я пришел сюда с тем же вопросом и обнаружил, что удаление __init__.pyиз моего каталога тестов решило его для меня.
101
3
@ mafro Я не вижу проблемы? Тесты не должны быть импортируемым кодом, они найдены вашим тестовым организатором. Только проверяемый код должен быть установленным пакетом / модулем, а не тестами.
К.-Майкл Ай
5
Добавление __init__.pyв подкаталоги in test/делает абсолютный импорт работающим для запуска определенных тестов в этом подкаталоге для устанавливаемых модулей. Спасибо.
Брайс Гуинта
7
вот и все: doc.pytest.org/en/latest/goodpractices.html действительно легко найти с помощью Google.
К.-Майкл Ай
46

Запустите pytestсебя как модуль с: python -m pytest tests

Стефано Мессина
источник
3
Это кажется рабочим решением, но кто-нибудь может объяснить, ПОЧЕМУ? Я предпочел бы исправить основную причину, чем просто использовать python -m pytestбез объяснения причин, кроме «потому что это работает»
Янне Энберг
4
Это происходит, когда иерархия проекта, например: package/src package/testsи testsвы импортируете из src. Выполнение в качестве модуля будет рассматривать импорт как абсолютный, чем относительно места выполнения.
Стефано Мессина
1
Это решение помогло мне, спасибо! Причина этого была из-за конфликта в версии Python. Тест Pytest работает для более ранней версии Python. В моей ситуации моя версия python 3.7.1, python -m pytest тесты работают, но не pytest тесты.
Рукси Чжан
1
Из Pytest «Запуск pytest с python -m pytest [...] вместо pytest [...] приводит к почти эквивалентному поведению, за исключением того, что предыдущий вызов добавит текущий каталог в sys.path».
Моад Эннаги
37

Вы можете запустить с PYTHONPATH в корне проекта

PYTHONPATH=. py.test

Или используйте pip install в качестве редактируемого импорта

pip install -e .   # install package using setup.py in editable mode
Форд Го
источник
3
Это не сработало для меня с testкаталогом, не srcвходящим в структуру каталогов и вызывающим из каталога, содержащего testи srcкаталог, и каталог.
Зельфир Кальцталь
21

Я создал это как ответ на ваш вопрос и мое собственное замешательство. Я надеюсь, что это помогает. Обратите внимание на PYTHONPATH как в командной строке py.test, так и в tox.ini.

https://github.com/jeffmacdonald/pytest_test

В частности: Вы должны указать py.test и tox, где найти модули, которые вы включаете.

С py.test вы можете сделать это:

PYTHONPATH=. py.test

А с помощью tox добавьте это в ваш tox.ini:

[testenv]
deps= -r{toxinidir}/requirements.txt
commands=py.test
setenv =
    PYTHONPATH = {toxinidir}
Джефф Макдональд
источник
1
Не могли бы вы дать краткое объяснение проекта, который вы связали?
JF Meier
1
Может быть, это только я, но README по проекту довольно подробный, и мой комментарий к stackoverflow говорит, почему я создал репо.
Джефф Макдональд
5
Хотя в этом нет особой необходимости, обычная политика заключается в том, чтобы основное содержание ответа содержалось в самом ответе, поскольку оно гарантирует, что ответ будет понятен через x лет, когда связанный ресурс может быть давно удален.
Дж. Ф. Мейер
:) Ну что ж. Вот интернет для тебя.
Джефф Макдональд
9

У меня была такая же проблема во Фляске.

Когда я добавил:

__init__.py

в папку тестов, проблема исчезла :)

Возможно, приложение не может распознать тесты папок как модуль

user13037517
источник
8

Я исправил это, удалив верхний уровень __init__.pyв родительской папке моих источников.

Гонсало
источник
1
Исправил это для меня. Может кто-нибудь объяснить это?
Абогер
это прямо здесь исправило это и для меня. определенно хотел бы видеть объяснение этому, если у кого-то есть это
king_wayne
я добавил init .py, но все еще сталкивался с теми же проблемами, но это решение сработало и для меня.
Абхиджит
7

Я начал получать странные ConftestImportFailure: ImportError('No module named ...ошибки, когда случайно добавил __init__.pyфайл в свой каталог src (который не должен был быть пакетом Python, а просто контейнером из всех источников).

jbasko
источник
3

Я получал эту ошибку из-за чего-то еще более простого (можно даже сказать тривиального). Я не установил pytestмодуль. Так простоapt install python-pytest исправил это для меня.

'pytest' был бы указан в setup.py как тестовая зависимость. Убедитесь, что вы также установили требования к тестированию.

craq
источник
3

У меня была похожая проблема. pytestне распознал модуль, установленный в среде, в которой я работал.

Я решил это, также установив pytestв ту же среду.

nocibambi
источник
Хотя я использовал pytest из venv, он также был установлен по всему миру, что дало мне эту ошибку. После удаления глобальной версии и установки внутри venv все заработало.
Маркус Ресел
2

Для меня проблема была tests.pyсгенерирована Django вместе с testsкаталогом. Удаление tests.pyрешило проблему.

Павел Муха
источник
2

Я получил эту ошибку, так как неправильно использовал относительный импорт. В примере OP test_app.py должен импортировать функции, используя, например,

from repo.app import *

Однако в общем случае __init__.py файлы разбросаны по файловой структуре, это не работает и создает вид импортируемой ошибки, если только файлы и тестовые файлы не находятся в одном каталоге.

from app import *

Вот пример того, что я должен был сделать с одним из моих проектов:

Вот моя структура проекта:

microbit/
microbit/activity_indicator/activity_indicator.py
microbit/tests/test_activity_indicator.py

Чтобы получить доступ к activity_indicator.py из test_activity_indicator.py, мне нужно было:

  • запустите test_activity_indicatory.py с правильным относительным импортом:
    from microbit.activity_indicator.activity_indicator import *
  • поместите файлы __init__.py по всей структуре проекта:
    microbit/
    microbit/__init__.py
    microbit/activity_indicator/__init__.py
    microbit/activity_indicator/activity_indicator.py
    microbit/tests/__init__.py
    microbit/tests/test_activity_indicator.py
Oppy
источник
0

Очень часто тесты прерывались из-за невозможности импорта модуля. После исследования я обнаружил, что система ищет файл в неправильном месте, и мы легко можем решить проблему, скопировав файл, содержащий модуль, в та же папка, как указано, для правильного импорта. Еще одно решение - изменить декларацию для импорта и показать MutPy правильный путь к блоку. Однако из-за того, что несколько модулей могут иметь эту зависимость, а это означает, что нам необходимо зафиксировать изменения и в их объявлениях, мы предпочитаем просто переместить модуль в папку.

Baydaa
источник
Есть другие ответы, которые предоставляют вопрос ОП, и они были опубликованы некоторое время назад. При публикации ответа обязательно добавьте новое решение или существенно лучшее объяснение, особенно при ответе на старые вопросы. Иногда лучше оставить комментарий к конкретному ответу.
help-info.de
Как дополнение к комментарию от @ help-info.de: Вот ссылка на руководство для ответов на вопросы: stackoverflow.com/help/how-to-answer
рука NOD
0

Согласно сообщению на Medium Дирка Эйвери (и подтверждается моим личным опытом), если вы используете виртуальную среду для своего проекта, то вы не можете использовать общесистемную установку pytest; Вы должны установить его в виртуальной среде и использовать эту установку.

В частности, если она установлена ​​в обоих местах, то просто запустить pytestкоманду не получится, поскольку она будет использовать установку системы. Как описано в других ответах, одно простое решение - запустить python -m pytestвместо pytest; это работает, потому что он использует версию среды pytest. Кроме того, вы можете просто удалить системную версию pytest; после реактивации виртуальной среды pytestкоманда должна работать.

Einhaender
источник
Единственная вещь, которая работала для меня до сих пор, была python -m pytest tests/.
Ник О'Лай
0

У меня возникла та же проблема, когда я следовал учебнику по Flask, и я нашел ответ в официальных документах Pytest. Это немного отличается от того, как я (и я думаю, что многие другие) используются для того, чтобы что-то делать.

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

from setuptools import setup, find_packages
setup(name="PACKAGENAME", packages=find_packages())

где PACKAGENAME - это имя вашего приложения. Затем вы должны установить его с помощью pip:

pip install -e .

-eФлаг говорит ПГИ intall пакета в редактируемом или «развивать» режим. Поэтому при следующем запуске pytestприложение должно найти ваше приложение в стандарте PYTHONPATH.

Луис Лезкано Айральди
источник
0

Мое решение:

создать conftest.pyфайл в testкаталоге, содержащем:

import os
import sys
sys.path.insert(0,os.path.dirname(os.path.realpath(__file__)) + "/relative/path/to/code/")

Это добавит интересующую папку к пути python без изменения каждого тестового файла , установки переменной env или работы с абсолютными / относительными путями.

Дэвид Бурба
источник