pytest 在 Lambda 函数中找不到模块

pytest can't find module in Lambda function

我有一个 Lambda,它是使用 SAM CLI 创建的,只有一个函数。该函数本身工作正常(在本地和部署时)但是 pytest 失败,表明我的函数无法在我的目录结构中导入模块。

注意目录结构显示在问题的底部。

# All commands are run from the 'Lambda' directory.
# With the 'sam' commands, the expected message is returned both locally and when deployed.

sam build -u
sam local invoke MyFunction --event events/405-get.json
sam deploy

# But the 'pytest' command fails.

python -m pytest tests/unit -v

我目前唯一的测试是确保HTTP方法不是GETDELETEPUT时返回错误。 from core.exception import MethodNotAllowedException 行确实在 'my-function/app.py' 的第 2 行,并且如前所述,当 运行 在本地或部署到 AWS 时,该函数能够毫无问题地导入此模块。

这是 pytest 遇到的错误。

my-function/app.py:2: in <module>
    from core.exception import MethodNotAllowedException
E   ModuleNotFoundError: No module named 'core'

我已尝试将模块移动到与 'app.py' 相同的目录,但导入仍然失败。

my-function/app.py:2: in <module>
    from exception import MethodNotAllowedException
E   ModuleNotFoundError: No module named 'exception'

我也尝试过使用相对导入,所以我将 'my-function/app.py' 的第 2 行更改为 from .core.exception import MethodNotAllowedException。然而,虽然 pytest 现在可以导入模块,但在使用 sam local invoke 时函数本身会失败。所以我认为相对进口是行不通的。

Unable to import module 'app': attempted relative import with no known parent package

如何让 pytest 使用我的函数?

test_handler.py

from proxy import app
import json
import os
import pytest


@pytest.fixture(scope='function')
def mock_context(mocker):
    mock_context = mocker.MagicMock()
    mock_context.aws_request_id = '00000000-0000-1000-0000-000000000000'
    return mock_context


@pytest.fixture(scope='function')
def event(request):
    dir = '{0}/../../events'.format(os.path.dirname(os.path.abspath(__file__)))
    name = request.param
    path = '{0}/{1}'.format(dir, name)
    with open(path) as f:
        event = f.read()
    return event


class TestMethodNotAllowed:

    @pytest.mark.parametrize(
        'event', ['405-get.json'], indirect=True)
    def test_405_get(self, event, mock_context):
        '''Test a GET event.'''

        response = app.lambda_handler(json.loads(event), mock_context)
        body = json.loads(response['body'])

        assert response['statusCode'] == 405
        assert 'exception' in body
        assert 'error_message' in body['exception']
        assert body['exception']['error_message'] == 'Method not allowed.'
        assert body['exception']['error_type'] == 'MethodNotAllowedException'
        assert body['exception']['http_method'] == 'GET'

    # Tests for PUT and DELETE also exist.

文件结构

Lambda
├── __init__.py
├── samconfig.toml
├── template.yaml
├── events
|   ├── 405-delete.json
|   ├── 405-get.json
|   └── 405-put.json
├── my-function
|   ├── init.py
|   ├── app.py
|   ├── requirements.txt
|   └── core
|       ├── __init__.py
|       ├── exception.py
|       ├── someotherfile.py
|       └── morefiles.py
└── test
    ├── __init__.py
    ├── requirements.txt
    └── unit
        ├── __init__.py
        └── test_handler.py

您可以使用 --import-mode 等命令行选项控制添加到 sys.path 的内容。可以在 https://docs.pytest.org/en/6.2.x/pythonpath.html.

找到更多信息

编辑

如果导入模式不起作用,也许您可​​以尝试在测试中导入之前自己添加路径,例如

import sys
sys.path.append('../../my-function')
...