skip to Main Content

I am trying to run a Python unit test within a docker image.

This is the Dockerfile used to create the image

FROM python:3.8-alpine

COPY lambda/ /app
WORKDIR /app
RUN pip install -r requirements.txt
RUN pip install unittest-xml-reporting

CMD []

and I try to run with

    docker run -i --rm 
        -v ${WORK_DIR}/lambda/junit-reports:/app/junit-reports 
        pd-engineering-metrics:test_python38 
        python -m xmlrunner --output-file junit-reports/test-file.xml *_test.py

The error I get is

Running tests...
----------------------------------------------------------------------
E
======================================================================
ERROR [0.002s]: *_test (unittest.loader._FailedTest)
----------------------------------------------------------------------
ImportError: Failed to import test module: *_test
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/unittest/loader.py", line 154, in loadTestsFromName
    module = __import__(module_name)
ModuleNotFoundError: No module named '*_test'
----------------------------------------------------------------------
Ran 1 test in 0.002s
FAILED (errors=1)

But, if I enter the running Image and manually trigger the test it works

docker run -it pd-engineering-metrics:test_python38 /bin/sh

/app # python -m xmlrunner --output-file junit-reports/test-file.xml *_test.py

Running tests...

Ran 15 tests in 0.024s

OK

Generating XML reports

Any idea what could be wrong here?

EDIT: I found a way to make it work, it doesn’t answer the question though.

Changing the command line to the following works

python -m xmlrunner --output-file junit-reports/test-file.xml discover -s /app -p "*_test.py"

2

Answers


  1. Normally if you run a command like python -m xmlrunner *_test.py, your local shell expands that wildcard into a list of filenames and explicitly passes them as arguments to the process. In the example you’ve shown, it looks like your test files are in a lambda subdirectory, so the wildcard doesn’t match anything. The shell passes the string *_test.py directly as an argument to the container, and the container directly runs the python command without invoking another shell, which results in the error you get.

    The "classic" workaround to this is to wrap your command in a sh -c invocation, single-quoting the command string

    docker run --rm ... pd-engineering-metrics:test_python38 
      sh -c 'python -m xmlrunner ... *_test.py'
    

    The approach you landed on uses the unittest -p option. Instead of passing the names of the test files, this passes in a pattern that matches their names. This avoids the problem of the host and container filesystems being different, and causes the lookup to happen inside the container filesystem. This will work fine too, and is probably a little easier than the sh -c wrapper.

    (I’d also consider running unit tests directly on the host, in a Python virtual environment, without involving Docker at all.)

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search