PyTest is a Python framework that allows for the creation of elegant, easy-to-read and grok unit tests using normal Python language. PyTest is a pretty intelligent framework that can seek out files containing tests (simply make sure the filename ends or begins with 'test') as well as the files they are testing, although this can run into limits when dealing with the types of file structures that normal software projects utilize. Take, for example, this pretty standard file structure:
The test file is contained within the tests folder, while the actual code being tested resides within the ./src/gutenbergPasswordGenerator folder. Pytest has several ways to account for this. First, when invoking pytest, invoking the location of the folder with the pytest command would run the tests appropriately and give you the correct result
pytest -m ./src/gutenbergPasswordGenerator
It is also possible to create a .ini file that specifies the locations your files are located
Finally, and this is the solution that I have used, it is also possible to append the file location your files are located to PYTHONPATH. This is typically recommended to either be done within the script scope or within a short-living process(such as a Docker container) to prevent this location from being accessed by other scripts unintentionally. The standard library sys
module allows you to quickly an easy append to PYTHONPATH as follows:
import sys
sys.path.insert(0,'./src/gutenbergPasswordGenerator')
With this in place, your test files will be able to properly access the other file locations to run tests as needed.
However, this is not without limitations. Since you need to append to PYTHONPATH before accessing the scripts within it, certain linting standards will not be adhered to (flake8 in my experience will not accept a script prepared in this way since a non-import statement appears before every other import statement). This can also be a problem when PyTest, as opposed to being ran locally on your development machine, is being processed on your code automatically as part of a CI/CD pipeline. GitHub Actions supports appending to PYTHONPATH as an environmental variable, which is how I circumvented the problem in my work. If this is a route that you are interested in going down, here's how I set up the YAML manifest:
name: Pytesting
on: [push]
env:
PYTHONPATH: "/home/runner/work/gutenbergPasswordGenerator/gutenbergPasswordGenerator/src/gutenbergPasswordGenerator"
With this complete, my PyTests would run even with the more complicated file structure and give me the desired results:
What are some of your favorite tricks to use PyTest? How do you arrange your file system to accomodate unit testing in your projects? I'd love to hear about it in the comments.