You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

pytest执行测试时遇ImportError:无法导入handler模块求助

pytest运行测试报错:ModuleNotFoundError: No module named '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 = src
    
    之后直接执行pytest即可,无需每次手动设置环境变量。

方案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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 19:05:13