I have a Lambda, created using the SAM CLI, with a single function. The function itself works just fine (both locally and when deployed) however pytest
fails, suggesting that my function is unable to import a module within my directory structure.
Note that the directory structure is shown at the bottom of the question.
# 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
The only tests I have at present are to ensure that an error is returned when the HTTP method is not GET
, DELETE
or PUT
. The line from core.exception import MethodNotAllowedException
is indeed at line 2 of 'my-function/app.py', and as mentioned previously, the function is able to import this module without issue when running locally or when deployed to AWS.
Here is the error that pytest
is encountering.
my-function/app.py:2: in <module>
from core.exception import MethodNotAllowedException
E ModuleNotFoundError: No module named 'core'
I have tried to move the modules to the same directory as 'app.py' but import still fails.
my-function/app.py:2: in <module>
from exception import MethodNotAllowedException
E ModuleNotFoundError: No module named 'exception'
I did also try to use relative imports, so I changed line 2 of 'my-function/app.py' to from.core.exception import MethodNotAllowedException
. However, while pytest
is now able to import the module, the function itself fails when using sam local invoke
. So relative imports I believe are a non-starter.
Unable to import module 'app': attempted relative import with no known parent package
How can I get pytest
to work with my function?
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
You can control what's added to sys.path
using command line options like --import-mode
. More information can be found at https://docs.pytest.org/en/6.2.x/pythonpath.html .
If the import modes didn't work perhaps you can try adding the path yourself before the imports in your test, something like
import sys
sys.path.append('../../my-function')
...
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.