pythonaws-lambdapytest

Configuring Pytest to Find Tests Across Multiple Project Directories


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!


Solution

  • 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!