How can I improve code coverage of Python3 - python

With unittest and Coverage.py,
def add_one(num: int):
num = num + 1
return num
from unittest import TestCase
from add_one import add_one
class TestAddOne(TestCase):
def test_add_one(self):
self.assertEqual(add_one(0), 1)
self.assertNotEqual(add_one(0), 2)
and here is the coverage:
How can I test the whole file?

Assuming that your test file is called test_one.py run this command in the same directory:
coverage run -m unittest test_one.py && coverage report
Result should look similar to this:
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Name Stmts Miss Cover
---------------------------------
add_one.py 3 0 100%
test_one.py 6 0 100%
---------------------------------
TOTAL 9 0 100%

You never call the test_add_one method.
Note how the function definition is executed, but not the body. To run your test, add a __main__ check and a TestSuite/TextTestRunner (https://docs.python.org/3/library/unittest.html)
from unittest import TestCase, TestSuite, TextTestRunner
from add_one import add_one
class TestAddOne(TestCase):
def test_add_one(self):
self.assertEqual(add_one(0), 1)
self.assertNotEqual(add_one(0), 2)
if __name__ == "__main__":
suite = TestSuite()
suite.addTest(TestAddOne("test_add_one"))
TextTestRunner().run(suite)
The result of
coverage run <file.py>
coverage html
# OR
coverage report -m
is all lines tested.

Related

pytest testing internal functions

Let's say that I have a package qaz:
qaz/
__init__.py
qaz.py
tests/
test_qaz.py
setup.py
Now, I want to test some internal qaz.py functions:
def _abc():
return 3
def def():
return _abc() + 2
but when I run pytest with tests like this:
from qaz.qaz import *
def test_abc():
assert _abc() == 3
def test_def():
assert def() == 5
My question is how to test _abc()?
Now I'm getting:
E NameError: name '_abc' is not defined
Oh, figured it out. You need to be specific, when it comes to the internal functions:
from qaz.qaz import _abc

How to mock.patch library function from within unit test

I have a module called learning that uses random.uniform(). I have a file called test_learning.py containing unit tests. When I run a unit test, I would like the code in learning to see the patched version of random.uniform(). How can I do this? Here is what I have currently.
import random
import unittest
import unittest.mock as mock
class TestLearning(unittest.TestCase):
def test_get_random_belief_bit(self):
with mock.patch('learning.random.uniform', mock_uniform):
bit = learning.get_random_belief_bit(0.4)
self.assertEqual(bit, 0)
But the test (sometimes) fails because learning.get_random_belief_bit() seems to be using the real random.uniform().
Unit test solution:
learning.py:
import random
def get_random_belief_bit(f):
return random.uniform()
test_learning.py:
import random
import unittest
import unittest.mock as mock
import learning
class TestLearning(unittest.TestCase):
def test_get_random_belief_bit(self):
with mock.patch('random.uniform', mock.Mock()) as mock_uniform:
mock_uniform.return_value = 0
bit = learning.get_random_belief_bit(0.4)
self.assertEqual(bit, 0)
mock_uniform.assert_called_once()
if __name__ == '__main__':
unittest.main()
unit test result with coverage report:
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Name Stmts Miss Cover Missing
---------------------------------------------------------------------------
src/stackoverflow/57874971/learning.py 3 0 100%
src/stackoverflow/57874971/test_learning.py 13 0 100%
---------------------------------------------------------------------------
TOTAL 16 0 100%

Running python scripts within a subpackage of my package

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()
Three things:
always run your tests scripts from root of your project. This simple rule does not harm anything and will simplify your scenario
prefer absolute imports
-m option for python expects module in dotted form
Applying this to your code
Modify 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()
Call it from python -m <module>
$ python -m farm.testpad.testpad
woof
Bonus - testing from nose
For my testing I use following pattern
keep all testing code in project subdir called test or better tests
use nose testing framework
Being 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.
Assert printed values are 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"
Final test case of my taste 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.
Selective call of test cases
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

Django and tests in docfiles

I'm having a small problem with my test suite with Django.
I'm working on a Python package that can run in both Django and Plone (http://pypi.python.org/pypi/jquery.pyproxy).
All the tests are written as doctests, either in the Python code or in separate docfiles (for example the README.txt).
I can have those tests running fine but Django just do not count them:
[vincent ~/buildouts/tests/django_pyproxy]> bin/django test pyproxy
...
Creating test database for alias 'default'...
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
But if I had some failing test, it will appear correctly:
[vincent ~/buildouts/tests/django_pyproxy]> bin/django test pyproxy
...
Failed example:
1+1
Expected nothing
Got:
2
**********************************************************************
1 items had failures:
1 of 44 in README.rst
***Test Failed*** 1 failures.
Creating test database for alias 'default'...
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
This is how my test suite is declared right now:
import os
import doctest
from unittest import TestSuite
from jquery.pyproxy import base, utils
OPTIONFLAGS = (doctest.ELLIPSIS |
doctest.NORMALIZE_WHITESPACE)
__test__ = {
'base': doctest.testmod(
m=base,
optionflags=OPTIONFLAGS),
'utils': doctest.testmod(
m=utils,
optionflags=OPTIONFLAGS),
'readme': doctest.testfile(
"../../../README.rst",
optionflags=OPTIONFLAGS),
'django': doctest.testfile(
"django.txt",
optionflags=OPTIONFLAGS),
}
I guess I'm doing something wrong when declaring the test suite but I don't have a clue what it is exactly.
Thanks for your help,
Vincent
I finally solved the problem with the suite() method:
import os
import doctest
from django.utils import unittest
from jquery.pyproxy import base, utils
OPTIONFLAGS = (doctest.ELLIPSIS |
doctest.NORMALIZE_WHITESPACE)
testmods = {'base': base,
'utils': utils}
testfiles = {'readme': '../../../README.rst',
'django': 'django.txt'}
def suite():
return unittest.TestSuite(
[doctest.DocTestSuite(mod, optionflags = OPTIONFLAGS)
for mod in testmods.values()] + \
[doctest.DocFileSuite(f, optionflags = OPTIONFLAGS)
for f in testfiles.values()])
Apparently the problem when calling doctest.testfile or doctest.testmod is that the tests are directly ran.
Using DocTestSuite/DocFileSuite builds the list and then the test runner runs them.

Doctest and relative imports

I'm having trouble using doctest with relative imports. The simple solution is just to get rid of the relative imports. Are there any others?
Say I have a package called example containing 2 files:
example/__init__.py
"""
This package is entirely useless.
>>> arnold = Aardvark()
>>> arnold.talk()
I am an aardvark.
"""
from .A import Aardvark
if __name__ == "__main__":
import doctest
doctest.testmod()
example/A.py
class Aardvark(object):
def talk(self):
print("I am an aardvark.")
If I now attempt
python example/__init__.py
then I get the error
Traceback (most recent call last):
File "example/__init__.py", line 8, in <module>
from .A import Aardvark
ValueError: Attempted relative import in non-package
Create another file my_doctest_runner.py:
if __name__ == "__main__":
import doctest
import example
doctest.testmod(example)
Execute my_doctest_runner.py to run doctests in example/__init__.py:
$ python2.7 my_doctest_runner.py
**********************************************************************
File "/tmp/example/__init__.py", line 4, in example
Failed example:
arnold.talk()
Expected:
I am an aaardvark.
Got:
I am an aardvark.
**********************************************************************
1 items had failures:
1 of 2 in example
***Test Failed*** 1 failures.
Pytest's --doctest-modules flag takes care of relative imports:
$ ls example/
A.py __init__.py
$ pytest --doctest-modules example
==================== test session starts ====================
...
example/__init__.py . [100%]
===================== 1 passed in 0.03s =====================
Just do
from A import Aardvark

Categories

Resources