pytest执行测试时遇ImportError:无法导入handler模块求助
错误信息
ImportError while importing test module '/path/to/test_handler.py'.
Hint: make sure your test modules/packages have valid Python names.Traceback:
\python3.10\latest\lib\importlib_init_.py:126: in import_module
return _bootstrap._gcd_import(name[level:], package, level)
src\keys\test_handler.py:7: in
from handler import LambdaS3ResourceClass
E ModuleNotFoundError: No module named 'handler'
用户疑问
- 不确定
__init__.py文件该保留还是删除 - 对Python模块命名、路径引用规则有疑惑,怀疑pytest导入逻辑异常
- 不确定是否和IntelliJ配置有关
项目结构
|-- public-key-fetcher |-- src |-- keys |-- __init__.py |-- handler.py |-- test_handler.py |-- __init__.py |-- Makefile |-- poetry.toml |-- poetry.lock |-- pyproject.toml
相关代码
handler.py
# handler.py from os import environ from boto3 import resource, client # Initialize the resource once per Lambda execution environment by using global scope. _LAMBDA_S3_RESOURCE = { "resource": resource('s3'), "bucket_name": environ.get("PUBLIC_KEYS_BUCKET") } class LambdaS3ResourceClass: def __init__(self, lambda_s3_resource): self.resource = lambda_s3_resource["resource"] self.bucket_name = lambda_s3_resource["bucket_name"] self.bucket = self.resource.Bucket(self.bucket_name) def fetch_keys(event, context): # Do something
test_handler.py
# test_handler.py import os from boto3 import resource, client from moto import mock_lambda from moto import mock_s3 from handler import LambdaS3ResourceClass @mock_s3 @mock_lambda class TestLambda: def setUp(self) -> None: self.test_s3_bucket_name = "unit_test_s3_bucket" os.environ["S3_BUCKET_NAME"] = self.test_s3_bucket_name s3_client = client('s3', region_name="us-east-1") s3_client.create_bucket(Bucket=self.test_s3_bucket_name) s3_client.put_object(Bucket="mockBucket", Key="dummy-oidc.json", Body="abc") mocked_s3_resource = {"resource": resource('s3'), "bucket_name": self.test_s3_bucket_name} self.mocked_s3_class = LambdaS3ResourceClass(mocked_s3_resource) def test_fetch_keys(self): body = self.mocked_s3_class.bucket.Object("dummy-oidc.json").get()['Body'].read() assert body == "abc"
解决方法
核心原因
Python导入模块时,会从sys.path列表中的路径查找。当前test_handler.py和handler.py同属keys包,但直接用from handler import ...属于绝对导入,Python会在全局路径中寻找handler模块,而非当前所在的keys目录。
方案1:改用相对导入
修改test_handler.py中的导入语句为相对导入,明确从当前包内导入:
from .handler import LambdaS3ResourceClass
方案2:将src目录加入Python路径
运行pytest时,把src目录添加到PYTHONPATH,让Python能识别到keys包:
- 终端直接运行:
# Linux/macOS PYTHONPATH=./src pytest src/keys/test_handler.py # Windows(PowerShell) $env:PYTHONPATH = ".\src"; pytest src/keys/test_handler.py - 或者在项目根目录创建
pytest.ini文件,添加配置:
之后直接执行[pytest] pythonpath = srcpytest即可,无需每次手动设置环境变量。
方案3:用Poetry运行pytest
因为项目使用Poetry管理依赖,直接通过Poetry运行pytest,它会自动处理Python路径:
poetry run pytest src/keys/test_handler.py
额外提示
- 你的项目中已经存在
__init__.py文件,这是正确的——它们用来标识src和keys是Python包,不需要删除。 - 在IntelliJ中,可将项目根目录设为
public-key-fetcher,然后右键src目录 → Mark Directory as → Sources Root,IDE会自动把src加入Python路径,既方便代码提示,也能直接在IDE中运行测试。
内容的提问来源于stack exchange,提问作者debarnacles

