46

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.

8
  • "Note that while that last case [...] is legal, it is certainly discouraged ("insane" was the word Guido used)." - PEP328 Commented Jan 31, 2014 at 22:24
  • 1
    Try invoking it as a package... stackoverflow.com/a/11536794/75033 Commented Jan 31, 2014 at 22:28
  • 1
    possible duplicate of Attempted relative import in non-package even with __init__.py Commented Jan 31, 2014 at 22:30
  • I need to run it through unittest, so I don't think that will work. When I run this: 'python -m tests.test_a' I get this error: 'ValueError: Attempted relative import beyond toplevel package'. Commented Feb 1, 2014 at 5:18
  • I think you can chain -m's? And check on what relative imports are relative to? The pythonpath? The current directory? What? I just take guido's view and rarely, if ever, use them. If your tests are testing the lib module then put them into the lib with the routines they are testing. Only tests that are testing the project belong at the top level there. Commented Feb 1, 2014 at 7:15

4 Answers 4

29

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.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks! This did help. I also benefitted greatly from the module section in Mark Lutz's 'Learning Python'. I realized that I need to make sure that imports work correctly from the directory in which the module will actually be called, not the directory in which the module lives. So, now I'm calling my test files from project/, and the import in test_a reads: 'import lib.lib_a' and 'import spd'. That, in combination with the simple way to run unittests from a higher level directory, enabled me to fix my code.
What does test.py contain?
In this case it'd be an executable file that runs the tests (something like unittest.main(), setting up your sys.path or whatever is required - it's not essential for something simple.
What does ´test_a´ and ´test_b´ contain? And how do they import the contents of ´lib_a´ and ´lib_b´ for testing?
Like @JJones mentioned, Mark Lutz's 'Learning Python' explains the module import process very well. Highly recommended read.
6

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

1 Comment

I would upvote this if the structure of test_a would also be present
2

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 ..

1 Comment

And how do the test scripts under /tests import from /package? Is it something like import package.foo?
0

Here is another method, you can add this line to the top of your file:

sys.path.append(os.path.join(os.path.dirname(__file__)))

This adds the current directory to the path so you can use absolute imports instead of relative.

This allows you to have a separate tests folder, or as in my case a folder that I package up as a lambda later but still have all the imports be correct when that folder becomes the root but still be able to test from a tests folder outside that directory.

1 Comment

If you're going to use absolute imports it's better to just use an editable install while you're developing (pip install --editable .). I prefer absolute imports because then I can run unit tests on feed versions of the packages as well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.