Vấn đề PATH với pytest 'ImportError: Không có mô-đun có tên YadaYadaYada'


231

Tôi đã sử dụng easy_install để cài đặt pytest trên máy mac và bắt đầu viết bài kiểm tra cho một dự án có cấu trúc tệp như vậy:

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

chạy py.testtrong thư mục repo, mọi thứ hoạt động như bạn mong đợi

nhưng khi tôi thử điều tương tự trên linux hoặc windows (cả hai đều có pytest 2.2.3 trên chúng), nó sẽ sủa bất cứ khi nào nó chạm vào lần nhập đầu tiên của nó từ đường dẫn ứng dụng của tôi. Nói ví dụfrom app import some_def_in_app

Tôi có cần chỉnh sửa PATH của mình để chạy py.test trên các hệ thống này không? Có ai có kinh nghiệm này?


4
Đây là cách để sửa nó với setuptools.
ederag

4
Vui lòng kiểm tra câu trả lời @hoefling và xem xét thay đổi câu trả lời được chấp nhận của bạn, nếu SO cho phép sau thời gian dài này: tốt hơn nhiều!
Davide

Câu trả lời:


91

Có, thư mục nguồn không nằm trong đường dẫn của Python nếu bạn cdđến thư mục kiểm tra.

Bạn có 2 sự lựa chọn:

  1. Thêm đường dẫn thủ công vào các tệp kiểm tra, đại loại như thế này:

    import sys, os
    myPath = os.path.dirname(os.path.abspath(__file__))
    sys.path.insert(0, myPath + '/../')
  2. Chạy thử nghiệm với var env PYTHONPATH=../.


11
Khi tôi cdvào một thư mục? tôi đang chạy py.testtừ gốc của tôi trừ khi tôi nhầm lẫn và ý bạn là pytest đi qua các thư mục của tôi
MattoTodd

nếu đó là một cdvấn đề, tôi cũng sẽ không đánh nó trên mac chứ?
MattoTodd

Ồ, tôi đã đọc sai và nghĩ rằng nó không hoạt động từ thư mục tests. vẫn là mẹo trong gợi ý 1 sẽ hoạt động. Tôi chỉ sử dụng Linux vì vậy tôi không thể giải thích hành vi trên các hệ điều hành khác.
Not_a_Golfer

Bạn có nhập như thế trên tất cả các tệp test.txt của mình không?
MattoTodd

4
có, nhưng cấu trúc thư mục của tôi thường hơi khác một chút - tôi thường giữ / src và / test dưới thư mục gốc.
Not_a_Golfer

275

Tôi không chắc tại sao py.test không thêm thư mục hiện tại vào chính PYTHONPATH, nhưng đây là một cách giải quyết (được thực hiện từ thư mục gốc của kho lưu trữ của bạn):

python -m pytest tests/

Nó hoạt động vì Python thêm thư mục hiện tại trong PYTHONPATH cho bạn.


2
Nó yêu cầu viết lại các lần nhập tương đối thành các lần nhập tuyệt đối, nếu bạn có mã để chạy ứng dụng không ở cấp độ, nơi bạn thực hiện lệnh từ đó. Ví dụ: project/test/all-my-testsproject/src/app.pyvì sự thay đổi đó, người ta cần gọi app.pygián tiếp bằng cách sử dụng một __main__.pytệp project/srcđể có thể sử dụng cuộc gọi python -m src. Những thứ khá lộn xộn như tôi có thể nói.
Zelphir Kaltstahl

3
@Zelphir: Sử dụng nhập khẩu tuyệt đối là một thực tiễn được khuyến nghị. Habnabit có một bài viết hay về cách thực hành tốt nhất về đóng gói: blog.habnab.it/blog/2013/07/21/python-packages-and-you và PEP8 nói rằng "không nên sử dụng nhập khẩu tương đối ngầm và đã bị xóa trong Python 3. " Xem: python.org/dev/peps/pep-0008 .
Apterx

1
@Apteryx Ý bạn là "dự án tuyệt đối" phải không? Bởi vì những thứ như /home/user/dev/projectxyz/src ...sẽ thực sự tồi tệ và không chạy trên các máy khác trong hầu hết các trường hợp. Tôi nghĩ điều tôi muốn nói là tôi phải luôn ghi toàn bộ gốc dự án vào đường dẫn mô-đun ngay cả khi một mô-đun nằm trong cùng thư mục với tệp chạy. Tôi không biết rằng đây được coi là thực tiễn tốt nhất, vì vậy đó là một chút thông tin hữu ích, cảm ơn. Tôi đồng ý với hầu hết pep8, mặc dù nó vẫn chưa hoàn hảo.
Zelphir Kaltstahl

1
@Zelphir, vâng, đó là những gì tôi muốn nói. Tôi tin rằng thuật ngữ nhập tuyệt đối trong Python luôn đề cập đến "dự án tuyệt đối". Xem: python.org/dev/peps/pep-0328/#rationale-for-absolute-imports . Trên thực tế, tôi khá chắc chắn rằng bạn không thể nhập từ các vị trí đường dẫn ngẫu nhiên, tuyệt đối, ít nhất là sử dụng cơ chế "nhập" mặc định.
Apterx

4
Tôi đã thêm __init__.pyvào các bài kiểm tra, điều đó đã giải quyết vấn đề. Bây giờ tôi có thể sử dụngpytest
Kiran Kumar Kotari

154

conftest giải pháp

Giải pháp ít xâm lấn nhất là thêm một tệp trống có tên conftest.pytrong repo/thư mục:

$ touch repo/conftest.py

Đó là nó. Không cần phải viết mã tùy chỉnh để xáo trộn sys.pathhoặc nhớ kéo PYTHONPATHtheo hoặc đặt__init__.py vào các thư mục không thuộc về nó.

Thư mục dự án sau đó:

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

Giải trình

pytesttìm kiếm các conftestmô-đun trên bộ sưu tập thử nghiệm để thu thập các móc và đồ đạc tùy chỉnh và để nhập các đối tượng tùy chỉnh từ chúng, pytestthêm thư mục mẹ của conftest.pyvàosys.path (trong trường hợp này làrepo thư mục).

Cấu trúc dự án khác

Nếu bạn có cấu trúc dự án khác, hãy đặt conftest.pythư mục gốc trong gói (gói chứa gói nhưng không phải là gói, vì vậy không chứa __init__.py), ví dụ:

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

src bố trí

Mặc dù cách tiếp cận này có thể được sử dụng với srcbố cục (vị trí conftest.pytrong srcthư mục):

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

hãy cẩn thận khi thêm vào srcđể PYTHONPATHgiảm thiểu ý nghĩa và lợi ích của srcbố cục! Bạn sẽ kết thúc với việc kiểm tra mã từ kho lưu trữ chứ không phải gói đã cài đặt. Nếu bạn cần làm điều đó, có lẽ bạn không cần srcthư mục nào cả.

Đi đâu từ đây

Tất nhiên, conftestcác mô-đun không chỉ là một số tệp để giúp phát hiện mã nguồn; đó là nơi tất cả các cải tiến dành riêng cho dự án của pytestkhung và tùy chỉnh bộ thử nghiệm của bạn xảy ra. pytestcó rất nhiều thông tin về conftestcác mô-đun nằm rải rác trong các tài liệu của họ ; bắt đầu với conftest.py: plugin mỗi thư mục cục bộ

Ngoài ra, SO có một câu hỏi tuyệt vời về conftestcác mô-đun: Trong py.test, việc sử dụng các tập tin conftest.py là gì?


2
@ aaa90210 mặc dù tôi không thể tái tạo vấn đề của bạn (nhập từ một conftest trong thư mục gốc hoạt động ở mọi cấp độ), bạn không bao giờ nên nhập từ các tệp conftest vì đây là tên dành riêng pytestvà khuyên bạn không nên làm như vậy. Bằng cách đó, bạn gieo hạt giống cho các lỗi trong tương lai. Tạo một mô-đun khác có tên utils.pyvà đặt mã để sử dụng lại trong các thử nghiệm ở đó.
cuốc

4
Đồng ý Đây là giải pháp duy nhất phù hợp với tôi.
130

4
nên hoàn toàn là câu trả lời được chấp nhận - cảm ơn bạn!
Martin Peck

2
Theo logic, conftest.pykhông thuộc về mã ứng dụng và, imo, đặt nó dưới src/là không chính xác.
Nik O'Lai

2
Câu trả lời này phải là một tiêu đề cố định trên SO. Cảm ơn rất nhiều.
Rigoberta Raviolini

119

Tôi đã từng gặp vấn đề tương tự. Tôi đã sửa nó bằng cách thêm một __init__.pytập tin trống vào teststhư mục của tôi .


77
Lưu ý rằng điều này KHÔNG được đề xuất bởi 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
Tôi đến đây với cùng một câu hỏi và thấy việc xóa __init__.pykhỏi thư mục kiểm tra của tôi đã giải quyết nó cho tôi.
101

3
@mafro Tôi không thấy vấn đề? Các bài kiểm tra không phải là mã có thể nhập được, chúng được tìm thấy bởi người chạy thử nghiệm của bạn. Chỉ có mã ĐỂ KIỂM TRA phải là gói / mô-đun được cài đặt, không phải là các thử nghiệm.
K.-Michael Aye

5
Thêm một __init__.pytrong các thư mục con test/làm cho việc nhập tuyệt đối hoạt động để chạy các kiểm tra cụ thể trong thư mục con đó đối với các mô-đun được cài đặt. Cảm ơn.
Bryce Guinta

7
ở đó bạn đi: doc.pytest.org/en/latest/goodpractices.html thực sự dễ dàng tìm thấy với google.
K.-Michael Aye

46

Tự chạy pytestnhư một mô-đun với: python -m pytest tests


3
Đây dường như là một giải pháp làm việc, nhưng bất cứ ai cũng có thể giải thích TẠI SAO? Tôi muốn khắc phục nguyên nhân cơ bản hơn là chỉ sử dụng python -m pytestmà không có bất kỳ lời giải thích nào ngoài "vì nó hoạt động"
Janne Enberg

4
Điều này xảy ra khi hệ thống phân cấp dự án là ví dụ: package/src package/testsvà trong testsbạn nhập từ src. Thực thi như một mô-đun sẽ coi nhập khẩu là tuyệt đối hơn so với vị trí thực hiện.
Stefano Messina

1
Giải pháp này đã giúp tôi, cảm ơn! Nguyên nhân của điều này là do xung đột trong phiên bản Python. thử nghiệm pytest hoạt động cho phiên bản python trước đó. Trong tình huống của tôi, phiên bản python của tôi là 3.7.1, các thử nghiệm pytest python -m hoạt động nhưng không thử nghiệm pytest.
R lửa Zhang

1
Từ Pytest "Chạy pytest với python -m pytest [...] thay vì pytest [...] mang lại hành vi gần như tương đương, ngoại trừ cuộc gọi trước sẽ thêm thư mục hiện tại vào sys.path."
Moad Ennagi

37

Bạn có thể chạy với PYTHONPATH trong dự án gốc

PYTHONPATH=. py.test

Hoặc sử dụng cài đặt pip như nhập khẩu có thể chỉnh sửa

pip install -e .   # install package using setup.py in editable mode

3
Điều đó không làm việc cho tôi với một testthư mục không có trong srccấu trúc thư mục và gọi từ thư mục chứa cả thư mục testsrcthư mục.
Zelphir Kaltstahl

21

Tôi đã tạo ra điều này như một câu trả lời cho câu hỏi của bạn và sự nhầm lẫn của riêng tôi. Tôi hy vọng nó sẽ giúp. Hãy chú ý đến PYTHONPATH trong cả dòng lệnh py.test và trong tox.ini.

https://github.com/jeffmacdonald/pytest_test

Cụ thể: Bạn phải nói với py.test và tox nơi tìm các mô-đun bạn đang đưa vào.

Với py.test bạn có thể làm điều này:

PYTHONPATH=. py.test

Và với độc tố, hãy thêm phần này vào tox.ini của bạn:

[testenv]
deps= -r{toxinidir}/requirements.txt
commands=py.test
setenv =
    PYTHONPATH = {toxinidir}

1
Bạn có thể đưa ra một lời giải thích ngắn cho dự án bạn liên kết?
JF Meier

1
Có lẽ đó chỉ là tôi, nhưng README trong dự án khá chi tiết và nhận xét của tôi về stackoverflow nói lý do tại sao tôi tạo ra repo.
Jeff MacDonald

5
Mặc dù không thực sự cần thiết, nhưng chính sách thông thường phải có nội dung chính của câu trả lời vì nó đảm bảo rằng câu trả lời có thể hiểu được trong x năm kể từ bây giờ khi tài nguyên được liên kết có thể không còn nữa.
JF Meier

:) Ồ tốt. Đó là internet cho ya.
Jeff MacDonald

9

Tôi đã có cùng một vấn đề trong Flask.

Khi tôi thêm:

__init__.py

để kiểm tra thư mục, vấn đề biến mất :)

Có lẽ ứng dụng không thể nhận ra các bài kiểm tra thư mục là mô-đun


8

Tôi đã sửa nó bằng cách xóa cấp cao nhất __init__.pytrong thư mục mẹ của các nguồn.


1
Đã sửa nó cho tôi. Ai đó có thể giải thích điều này?
kẻ bắt cóc

Điều này ngay tại đây đã sửa nó cho tôi. chắc chắn rất thích xem một lời giải thích cho điều này nếu bất cứ ai có nó
king_wayne

tôi đã thêm init .py nhưng vẫn gặp phải vấn đề tương tự nhưng giải pháp này cũng hiệu quả với tôi .. lý do xin vui lòng?
Abhijit

7

Tôi bắt đầu nhận được các ConftestImportFailure: ImportError('No module named ...lỗi lạ khi tôi vô tình thêm __init__.pytệp vào thư mục src của mình (không được coi là gói Python, chỉ là một thùng chứa của tất cả các nguồn).


3

Tôi đã nhận được lỗi này do một cái gì đó thậm chí còn đơn giản hơn (thậm chí bạn có thể nói tầm thường). Tôi đã không cài đặt pytestmô-đun. Vì vậy, một đơn giản apt install python-pytestcố định nó cho tôi.

'pytest' sẽ được liệt kê trong setup.py dưới dạng phụ thuộc kiểm tra. Hãy chắc chắn rằng bạn cài đặt các yêu cầu kiểm tra là tốt.


3

Tôi đã có một vấn đề tương tự. pytestkhông nhận ra một mô-đun được cài đặt trong môi trường tôi đang làm việc.

Tôi đã giải quyết nó bằng cách cài đặt pytestvào cùng một môi trường.


Mặc dù tôi đang sử dụng pytest từ bên trong một venv, tôi cũng đã cài đặt nó trên toàn cầu và gây ra lỗi này. Sau khi gỡ cài đặt phiên bản toàn cầu và cài đặt bên trong venv, nó đã hoạt động.
Markus Ressel

2

Đối với tôi, vấn đề được tests.pytạo ra bởi Django cùng với teststhư mục. Loại bỏ tests.pygiải quyết vấn đề.


2

Tôi đã nhận được lỗi này khi tôi sử dụng nhập khẩu tương đối không chính xác. Trong ví dụ OP, test_app.py nên nhập các hàm bằng vd

from repo.app import *

Tuy nhiên, các tệp __init__.py tự do nằm rải rác xung quanh cấu trúc tệp, điều này không hoạt động và tạo ra loại Nhập khẩu được nhìn thấy trừ khi các tệp và tệp thử nghiệm nằm trong cùng một thư mục.

from app import *

Đây là một ví dụ về những gì tôi phải làm với một trong những dự án của mình:

Đây là cấu trúc dự án của tôi:

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

Để có thể truy cập Activity_indicator.py từ test_activity_indicator.py tôi cần:

  • bắt đầu test_activity_indicatory.py với nhập tương đối chính xác:
    from microbit.activity_indicator.activity_indicator import *
  • đặt các tệp __init__.py trong toàn bộ cấu trúc dự án:
    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

0

Rất thường các bài kiểm tra bị gián đoạn do không thể nhập mô-đun, Sau khi nghiên cứu, tôi phát hiện ra rằng hệ thống đang xem sai tệp và chúng tôi có thể dễ dàng khắc phục sự cố bằng cách sao chép tệp, có chứa mô-đun, trong cùng một thư mục như đã nêu, để được nhập chính xác. Một đề xuất giải pháp khác là thay đổi khai báo cho việc nhập và hiển thị MutPy đường dẫn chính xác của đơn vị. Tuy nhiên, do thực tế là nhiều đơn vị có thể có sự phụ thuộc này, có nghĩa là chúng ta cần phải thực hiện các thay đổi trong khai báo của chúng, chúng tôi chỉ muốn chuyển đơn vị vào thư mục.


Có những câu trả lời khác cung cấp câu hỏi của OP và chúng đã được đăng cách đây một thời gian. Khi đăng câu trả lời, vui lòng đảm bảo bạn thêm một giải pháp mới hoặc giải thích rõ ràng hơn, đặc biệt là khi trả lời các câu hỏi cũ hơn. Đôi khi tốt hơn là gửi bình luận về một câu trả lời cụ thể.
help-info.de

Ngoài nhận xét từ @ help-info.de: Đây là liên kết đến hướng dẫn trả lời câu hỏi: stackoverflow.com/help/how-to-answer
bàn tay của NOD

0

Theo một bài đăng trên Medium của Dirk Avery (và được hỗ trợ bởi kinh nghiệm cá nhân của tôi) nếu bạn đang sử dụng môi trường ảo cho dự án của mình thì bạn không thể sử dụng cài đặt pytest trên toàn hệ thống; bạn phải cài đặt nó trong môi trường ảo và sử dụng cài đặt đó.

Đặc biệt, nếu bạn đã cài đặt nó ở cả hai nơi thì chỉ cần chạy pytestlệnh sẽ không hoạt động vì nó sẽ sử dụng cài đặt hệ thống. Như các câu trả lời khác đã mô tả, một giải pháp đơn giản là chạy python -m pytestthay vì pytest; cái này hoạt động vì nó sử dụng phiên bản pytest của môi trường. Ngoài ra, bạn chỉ có thể gỡ cài đặt phiên bản pytest của hệ thống; Sau khi kích hoạt lại môi trường ảo, pytestlệnh sẽ hoạt động.


Điều duy nhất làm việc cho tôi cho đến nay là python -m pytest tests/.
Nik O'Lai

0

Tôi đã gặp vấn đề tương tự khi làm theo hướng dẫn Flask và tôi đã tìm thấy câu trả lời trên các tài liệu chính thức của Pytest Đó là một chút thay đổi so với cách tôi (và tôi nghĩ rằng nhiều người khác) đã từng làm việc này.

Bạn phải tạo ra một setup.py tệp trong thư mục gốc của dự án với ít nhất hai dòng sau:

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

trong đó PACKAGENAME là tên ứng dụng của bạn. Sau đó, bạn phải cài đặt nó với pip:

pip install -e .

Các -elá cờ nói pip để intall gói trong có thể chỉnh sửa hoặc "phát triển" chế độ. Vì vậy, lần tới khi bạn chạy, pytestbạn nên tìm ứng dụng của mình theo tiêu chuẩn PYTHONPATH.


0

Giải pháp của tôi:

tạo conftest.pytập tin trong testthư mục chứa:

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

Điều này sẽ thêm thư mục quan tâm vào đường dẫn python mà không sửa đổi mọi tệp kiểm tra , đặt biến env hoặc làm rối với các đường dẫn tuyệt đối / tương đối.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.