Can't Create Python Project Tests via Hitchhiker's Guide To Python Method

Viewed 150

I'm trying to build a project via the method described in The Hitchhiker's Guide To Python. I'm running into problems with the test structure.

My file structure looks like this:

.
├── __init__.py
├── sample
│   ├── __init__.py
│   └── core.py
├── setup.py
└── tests
    ├── __init__.py
    ├── context.py
    └── test_core.py

With:

# sample/core.py

class SampleClass:
    def got_it(self):
        return True 

And:

# tests/context.py

import os
import sys
sys.path.insert(0, os.path.abspath(
    os.path.join(os.path.dirname(__file__), '..')
))

import sample

And:

# tests/test_core.py

import unittest

from .context import sample


class SampleClassTest(unittest.TestCase):
    def test_got_it(self):
        # ...
        pass 
    
if __name__ == '__main__':
    unittest.main()

(Note that I just thru the __init__.py files in the root and tests to see if that helped, but it didn't.)

When I try to run tests/test_core.py with Python 3.7. I get this error:

ImportError: attempted relative import with no known parent package

That happens if I run the test file from outside the tests directory or in it.

If I remote the . and do this in tests/test_core.py:

from context import sample

Everything loads, but I can't access SampleClass.

The things I've tried are:

sc = SampleClass()
NameError: name 'SampleClass' is not defined

sc = sample.SampleClass()
AttributeError: module 'sample' has no attribute 'SampleClass'

sc = sample.core.SampleClass()
AttributeError: module 'sample' has no attribute 'core'

sc = core.SampleClass()
NameError: name 'core' is not defined

I tried on Python 2 as well and had similar problems (with slightly different error methods). I also tried calling a function instead of a class and had similar problems there as well.

Can someone point me in the direction of what I'm doing wrong?

2 Answers

This once caused me some headaches, until I realized that it just depended on the way the test is started. If you use python tests/test_core.py, then you are starting a simple script outside any package. So relative imports are forbidden.

But if (still from the project root folder) you use:

python -m tests.test_core

then you are starting the module test_core from the tests package, and relative imports are allowed.

From that time, I always start my tests as modules and not as scripts. What is even better is that unittest discover knows about packages. So if you are consistent in naming the test folder and scripts with a initial test, you can forget about the number of tests and just use:

python -m unittest discover

and that is enough to run the whole test suite.

Happy testing!

Since you have a proper package complete with setup.py, you're better off using pip install -e . instead of using a sys.path hack.

Minimal setup.py

#setup.py
from setuptools import setup, find_packages
setup(name='sample', version='0.0.1', packages=find_packages(exclude=('tests')))

Then you should import everything from sample packages anywhere you want using the same syntax (i.e. absolute path). To import SampleClass for example:

from sample.core import SampleClass

Also, to answer your question on why you're getting on relative import:

ImportError: attempted relative import with no known parent package

You'd have to avoid invoking the file as a script using python ... because relative imports do not work on scripts. You should invoke the file as a module using python -m .... Here is a very nice explanation that's been viewed a billion times:

Related