How does Python's unittest library match patterns passed via the -p parameter?

Viewed 808

I am running the following command to run only the tests located inside the file called test_CO2.py

python3.7 -m unittest discover -s some_path/tests/ -p "*CO2*"

My folder structure looks like the following:

some_path/tests/
  CO2
    test_CO2.py
  battery
    test_battery.py
  tank
    test_tank.py

I want to specify the tests that are ran. If for example I only wish to test the tank and CO2 code how can I do that? I thought of passing the following regex: \w*(CO2|tank)\w*.py which fails to find any tests.

I am thinking that the pattern passed in to the -p option does not accept regex. How, then can I specify the tests I wish to run?

2 Answers

Normally, everything you pass via the -p parameter into unittest is processed via TestLoader::_match_path() method which then invokes chain of functions fnmatch() → fnmatchcase() → _compile_pattern() → translate() from fnmatch library.

The translate() function translates your original -p argument into a regex, which is then used for name-matching.

The docs for fnmatch() function state this:

Patterns are Unix shell style:
*       matches everything
?       matches any single character
[seq]   matches any character in seq
[!seq]  matches any char not in seq

From what I can see, this is the extent of what it can do. All other characters are escaped to be matched literally.

Example: I passed regex a|b as the pattern. The translate() function returned final regex in the form (?s:p\|m)\Z. There the pipe character became escaped.

If you're extra curious, go see fnmatch lib's translate() function here - if you want to know the exact process of translating your "glob-like" patterns into the final regex.

I figured out how to bypass this restriction from unittest.

I can run specific tests with python3.7 -m unittest path_to_some_test

# take regex and find all files that match
# clean up results. i.e. remove './' from output
test_paths_result=()
test_paths=$(find . -regextype posix-extended -regex "${regex}")
for file_path in ${test_paths}; do
  # save clean results to array variable
  test_paths_result+=("${file_path:2}")
done

echo "Running test files that match the following expression: '${regex}'"
python3.7 -m unittest ${test_paths_result[@]}
Related