I'm looking to unit test all my AWS Lambda code in my project using pytest. Due to how I have to configure the directory structure to work with infrastructure as code tooling, each Lambda sits within it's own CloudFormation stack, I've got a pretty non-standard directory structure. I'm unable to get pytest to run all tests across all my Lambda functions - ideally I'd like this to work by just running 'pytest' in the root directory of the project.
The directory structure is as follows (it's worth noting that changing the structure is not an option):
- Project_Directory
- stack1
- product.template.yaml
- src
- lambda1
- lambda_function.py
- requirements.txt
- tests
- __init__.py
- test_functions
- __init__.py
- test_lambda1.py
- stack2
- product.template.yaml
- src
- lambda2
- lambda_function.py
- requirements.txt
- tests
- __init__.py
- test_functions
- __init__.py
- test_lambda2.py
- conftest.py
- pytest.ini
Each test_lambda.py file imports from the lambda_function.py file as follows:
from src.lambdax.lambda_function import func1, func2
When only a single stack is in the project directory pytest has no issue picking up the tests. However when a second stack is added pytest fails with the following error:
ModuleNotFoundError: No module named 'tests.test_functions.test_lambda2'
Also, when running pytest directly against each stack directory the tests run with no issue. That is running pytest stack1
and pytest stack2
.
Expectation: Running 'pytest' from project_directory yields all tests for all lambdas.
I've tried adding testpaths = stack1 stack2
and testpaths = stack1/tests stack2/tests
to pytest.ini to no success.
What am I missing here, I'm guessing maybe some module namespace collision but I'm not sure how to resolve it! Any advice on this issue is much appreciated!
Edit: This definitely looks to be a tests module namespace collision. After modifying the tests directory in stack2 to be called tests2 all tests run as expected.
I'm still keen for advice here, I'd really like to avoid enforcing each stack to have a different name for the tests directory!
Managed to solve this after painstakingly going through different approachs.
The solution is a combination of using the new(ish) importlib
import mode and having some custom sys.path
manipulation in conftest.py
.
Configure pytest.ini
as this:
[pytest]
addopts = --import-mode=importlib
Add the following code into conftest.py
:
def add_path(directory):
src_path = os.path.abspath(directory)
if src_path not in sys.path:
sys.path.insert(0, src_path)
add_path(os.path.join(os.path.dirname(__file__), "stack1"))
add_path(os.path.join(os.path.dirname(__file__), "stack2"))
The above conftest.py
code could definitely be written a bit nicer using PathLib
. I'll leave that as an exercise to the reader!
Another nice consequence to using the importlib
import mode is that the __init__.py
files are no longer required in the tests
and test_functions
directories in each stack!