Relative imports with unittest in Python

Question:

I am trying to use Python unittest and relative imports, and I can’t seem to figure it out. I know there are a lot of related questions, but none of them have helped so far. Sorry if this is repetitive, but I would really appreciate any help. I was trying to use the syntax from PEP 328 http://www.python.org/dev/peps/pep-0328/ but I must have something wrong.

My directory structure is:

project/
    __init__.py
    main_program.py
    lib/
        __init__.py
        lib_a
        lib_b
    tests/
        __init__.py
        test_a
        test_b

I run my tests using:

python -m unittest test_module1 test_module2

test_a needs to import both lib/lib_a and main_program. This is the code from test_a I am trying to use for the import:

from ..lib import lib_a as lib
from ...project import main_program

both raise this error:

ValueError: Attempted relative import in non-package

All of my init.py files are currently empty.

Any specific advice would be greatly appreciated!!

Edit:

This may be the answer: Python Packages?
I’m still verifying if this will work.

Edit II:

To clarify, at this point I have attempted to run my test file in 3 different ways:

project/tests $ python -m unittest test_a
project/tests $ python -m test_a
project/tests $ ./test_a

All three fail with the same error as above. When I use the same three syntaxes but in the project directory, I get this error:

ValueError: Attempted relative import beyond toplevel package

Thanks again.

Asked By: J Jones

||

Answers:

In my experience it is easiest if your project root is not a package, like so:

project/
  test.py
  run.py
  package/
    __init__.py
    main_program.py
    lib/
      __init__.py
      lib_a
      lib_b
    tests/
      __init__.py
      test_a
      test_b

However, as of python 3.2 , the unittest module provides the -t option, which lets you set the top level directory, so you could do (from package/):

python -m unittest discover -t ..

More details at the unittest docs.

Answered By: kai

I run with the same problem and kai’s answer solved it. I just want to complement his answer with the content of test.py (as @gsanta asked). I’ve only tested it on Python 2.7:

from packages.tests import test_a, test_b
import unittest

# for test_a
unittest.main(test_a, exit=False)

# for test_b
unittest.main(test_b)

then you can just

../project $ python test.py
Answered By: Rodrigo E. Principe

In a layout where tests and package are at sibling level:

/project
   /tests
   /package

One way is to start within the package dir, use -s to discover only in tests, and use -t to set the top level:

../package $ python3 -m unittest discover -s ../tests -t ..

Answered By: Eric Jarvi