Having some trouble figuring out the correct, python 2.x preferred way to do relative imports so that I can keep test scripts together in one subpackage, and have those test scripts be able to test my library.
$ farm\testpad\testpad.py
Traceback (most recent call last):
File "C:\farm\testpad\testpad.py", line 4, in <module>
from ..animals.dog import dog
ValueError: Attempted relative import in non-package
$ python -m farm\testpad\testpad
C:\Python27\python.exe: No module named farm\testpad\testpad
In the following example, what do I need to modify to do what I want? Please, and thanks for any help given.
e.g.
Package structure:
farm/
__init__.py # empty
animals/
__init__.py # empty
dog.py
testpad/
__init__.py # empty
testpad.py
dog.py
import sys
import os
class dog():
def __init__(self, name):
self.name = name
def speak(self):
print "woof"
testpad.py
import os
import sys
from ..animals.dog import dog
# create a dog and test its speak action
def testpad():
d = dog("Allen")
d.speak()
if __name__ == "__main__":
testpad()
Python subpackages To access subpackages, we use the dot operator. This is the __init__.py file in the constants directory. We import the names tuple. This is the data.py module in the constants directory.
To instruct Python to treat a folder containing files as a package, you need to create a __init__.py file in the folder. Note that starting with Python 3.3, Python introduced the implicit namespace packages feature. This allows Python to treat a folder as a package without the __init__.py .
Creating Packages Whenever you want to create a package, then you have to include __init__.py file in the directory. You can write code inside or leave it as blank as your wish. It doesn't bothers Python. Create a directory and include a __init__.py file in it to tell Python that the current directory is a package.
Three things:
-m
option for python
expects module in dotted formApplying this to your code
testpad.py
import os
import sys
from farm.animals.dog import dog
# Create a dog and test its speak action
def testpad():
d = dog("Allen")
d.speak()
if __name__ == "__main__":
testpad()
python -m <module>
$ python -m farm.testpad.testpad
woof
nose
For my testing I use following pattern
test
or better tests
nose
testing frameworkBeing in root of your project
Install nose
:
$ pip install nose
What creates a command nosetests
Reorganize your code
$ mkdir tests
$ mv farm/testpad/testpad.py tests/test_animals.py
$ rm -rf farm/testpad/
Run the tests (currently just one)
The simples way, wich does the work, but tries to keep the output minimal:
$ nosetests
.
----------------------------------------------------------------------
Ran 1 test in 0.003s
OK
As you see, nose is discovering test cases on his own (searching for whatever starts with test
)
Make it a bit more verbose:
$ nosetests -v
test_animals.testpad ... ok
----------------------------------------------------------------------
Ran 1 test in 0.003s
OK
Now you know, what tests were run.
To see captured outut, use -s
switch:
$ nosetests -vs
test_animals.testpad ... woof
ok
----------------------------------------------------------------------
Ran 1 test in 0.003s
OK
Add more tests to test_animals.py
import os
import sys
# create a dog and test its speak action
def test_dog():
from farm.animals.dog import dog
d = dog("Allen")
d.speak()
def test_cat():
from farm.animals.dog import cat
c = cat("Micy")
d.speak()
def test_rabbit():
from farm.animals.dog import rabbit
r = rabbit()
r.speak()
Test it:
$ nosetests -vs
test_animals.test_dog ... woof
ok
test_animals.test_cat ... ERROR
test_animals.test_rabbit ... ERROR
======================================================================
ERROR: test_animals.test_cat
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/javl/Envs/so/local/lib/python2.7/site-packages/nose/case.py", line 197, in runTest
self.test(*self.arg)
File "/home/javl/sandbox/so/testpad/tests/test_animals.py", line 12, in test_cat
from farm.animals.dog import cat
ImportError: cannot import name cat
======================================================================
ERROR: test_animals.test_rabbit
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/javl/Envs/so/local/lib/python2.7/site-packages/nose/case.py", line 197, in runTest
self.test(*self.arg)
File "/home/javl/sandbox/so/testpad/tests/test_animals.py", line 17, in test_rabbit
from farm.animals.dog import rabbit
ImportError: cannot import name rabbit
----------------------------------------------------------------------
Ran 3 tests in 0.004s
FAILED (errors=2)
Add --pdb
switch to jump to debugger (if you install ipdbplugin
, you might use --ipdb
):
$ nosetests -vs --pdb
test_animals.test_dog ... woof
ok
test_animals.test_cat ... > /home/javl/sandbox/so/testpad/tests/test_animals.py(12)test_cat()
-> from farm.animals.dog import cat
(Pdb) l
7 from farm.animals.dog import dog
8 d = dog("Allen")
9 d.speak()
10
11 def test_cat():
12 -> from farm.animals.dog import cat
13 c = cat("Micy")
14 d.speak()
15
16 def test_rabbit():
17 from farm.animals.dog import rabbit
(Pdb)
Use h
or find some tutorial for debugger, it is great tool
Real test cases shall assert that behaviour is as expected.
As you are printing to stdout, we can capture the output using mocking (in Python 2.x you have to install it, in Python 3.x you from unittest import mock
):
$ pip install mock
And modify your test_animals.py
:
from mock import patch
from StringIO import StringIO
# create a dog and test its speak action
@patch("sys.stdout", new_callable=StringIO)
def test_dog(mock_stdout):
from farm.animals.dog import Dog
d = Dog("Allen")
d.speak()
assert mock_stdout.getvalue() == "woof\n"
@patch("sys.stdout", new_callable=StringIO)
def test_cat(mock_stdout):
from farm.animals.cat import Cat
c = Cat("Micy")
c.speak()
assert mock_stdout.getvalue() == "mnau\n"
@patch("sys.stdout", new_callable=StringIO)
def test_rabbit(mock_stdout):
from farm.animals.rabbit import Rabbit
r = Rabbit("BB")
r.speak()
assert mock_stdout.getvalue() == "Playboy, Playboy\n"
test_mytaste.py
def test_dog():
from farm.animals.dog import Dog
d = Dog("Allen")
sound = d.speak()
assert sound == "woof", "A dog shall say `woof`"
def test_cat():
from farm.animals.cat import Cat
c = Cat("Micy")
sound = c.speak()
assert sound == "mnau", "A cat shall say `mnau`"
def test_rabbit():
from farm.animals.rabbit import Rabbit
r = Rabbit("BB")
sound = r.speak()
assert sound == "Playboy, Playboy", "A Rabbit shall say ..."
This requires you refactor your code (class names starting uppercase, speak
method not printing but returning the sound).
In fact, you may start writing your code from test cases and adding tested modules later on, this often leads to nicer design as you start thinking of real use from the very beginning.
nose is great in searching for test cases (generally whatever starts with test
), but sometime you might focus on particular test or use differently named python file. You can tell nose
to use just one test file:
$ nosetests -vs tests/test_mytaste.py
test_mytaste.test_dog ... ok
test_mytaste.test_cat ... ok
test_mytaste.test_rabbit ... ok
----------------------------------------------------------------------
Ran 3 tests in 0.002s
OK
or even target nose
to run specific test from it:
$ nosetests -vs tests/test_mytaste.py:test_dog
test_mytaste.test_dog ... ok
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With