I was wondering how can I unit test if a recursive function has been called correctly. For example this function:
def test01(number):
if(len(number) == 1):
return 1
else:
return 1+test01(number[1:])
It counts recursvely how many digits a number has (assuming the number type is string)
So, I want to test if the function test01 has been called recursively. It would be ok if it is implemented just like that, but not if it is implemented as:
def test01(number):
return len(number)
EDIT:
The recursive approach is mandatory for educational purposes, so the UnitTest process will automate programming exercises checking. Is there a way to check if the function was called more than once? If that is possible, I can have 2 tests, one asserting the correct output and one to check if the function was called more than once for the same input.
Thank you in advance for your help
Guessing by the tags I assume you want to use unittest to test for the recursive call. Here is an example for such a check:
from unittest import TestCase
import my_module
class RecursionTest(TestCase):
def setUp(self):
self.counter = 0 # counts the number of calls
def checked_fct(self, fct): # wrapper function that increases a counter on each call
def wrapped(*args, **kwargs):
self.counter += 1
return fct(*args, **kwargs)
return wrapped
def test_recursion(self):
# replace your function with the checked version
with mock.patch('my_module.test01',
self.checked_fct(my_module.test01)): # assuming test01 lives in my_module.py
result = my_module.test01('444') # call the function
self.assertEqual(result, 3) # check for the correct result
self.assertGreater(self.counter, 1) # ensure the function has been called more than once
Note: I used import my_module instead of from my_module import test01 so that the first call is also mocked - otherwise the number of calls would be one too low.
Depending on how your setup looks like, you may add further tests manually, or auto-generate the test code for each test, or use parametrization with pytest, or do something else to automate the tests.
Normally a unit test should check at least that your function works and try to test all code paths in it
Your unit test should therefore try to take the main path several times, and then find the exit path, attaining full coverage
You can use the 3rd-party coverage module to see if all your code paths are being taken
pip install coverage
python -m coverage erase # coverage is additive, so clear out old runs
python -m coverage run -m unittest discover tests/unit_tests
python -m coverage report -m # report, showing missed lines
Curtis Schlak taught me this strategy recently.
It utilizes Abstract Syntax Trees and the inspect module.
All my best,
Shawn
import unittest
import ast
import inspect
from so import test01
class Test(unittest.TestCase):
# Check to see if function calls itself recursively
def test_has_recursive_call(self):
# Boolean switch
has_recursive_call = False
# converts function into a string
src = inspect.getsource(test01)
# splits the source code into tokens
# based on the grammar
# transformed into an Abstract Syntax Tree
tree = ast.parse(src)
# walk tree
for node in ast.walk(tree):
# check for function call
# and if the func called was "test01"
if (
type(node) is ast.Call
and node.func.id == "test01"
):
# flip Boolean switch to true
has_recursive_call = True
# assert: has_recursive_call should be true
self.assertTrue(
has_recursive_call,
msg="The function does not "
"make a recursive call",
)
print("\nThe function makes a recursive call")
if __name__ == "__main__":
unittest.main()
Related
I have the following:
def func():
s = 1
i = -1
while i != 0:
s += i
i = int(input())
return s
if __name__ == "__main__":
result = func()
print(str(result))
You will see that there is a single call to the function, but the function contains a loop that iterates until the use enters a value of 0.
How do I test this function with unittest library?
I am assuming your code is inside a module called mymodule.py. Therefore, you could create a test file name test_mymodule.py to implement your tests. What you want to do is to use the unittest.mock module to have access to the patch() function in order to decorate the builtin input.
What does that mean is that instead of calling the input function to ask for the user input, you are patching it to return the values defined in side_effect. Each call of input will therefore return a value of the list. Notice that you should include 0 as well, otherwise the test will not work.
For each sequence of inputs, you will have to compute manually (or even using your program) to provide the final result for the method assertEqual.
import unittest
import unittest.mock
from mymodule import func
class TestModule(unittest.TestCase):
#unittest.mock.patch('builtins.input', side_effect=[1, 2, 3, 0])
def test_func_list1(self, mock):
self.assertEqual(func(), 6)
#unittest.mock.patch('builtins.input', side_effect=[0])
def test_func_list2(self, mock):
self.assertEqual(func(), 0)
Each test method should be prefixed with a test_ in its name. The default pattern when using python -m unittest from the CLI looks for test*.py in the current directory (it is the same as running TestLoader.discover(). You can probably change this if you want, but you will have to take a look at the unittest documentation for more details.
Example
def main(p):
if foo_a(p):
return False
return p**2
def foo_a(p):
return p % 11 == 0
Now you can get 100% test coverage by
import unittest
from script import main
class Foobar(unittest.TestCase):
def test_main(self):
self.assertEquals(main(3), 9)
But maybe one wanted foo_a to be p % 2 == 0 instead.
The question
Branch coverage would shed a light on it, but I would also like to know if a function was never called "directly" by a test (such as main is in the example), but only indirectly (such as foo_a in the example).
Is this possible with pytest?
First of all just general line of thought is to unittest foo_a as well
import unittest
from script import main, foo_a
class Foobar(unittest.TestCase):
def test_main(self):
self.assertEquals(main(3), 9)
def test_foo_a(self):
self.assertEquals(foo_a(11), True)
You are probably looking for https://coverage.readthedocs.io/en/coverage-4.5.1/ which can be used with pytest https://pypi.org/project/pytest-cov/, this tool can show you exactly which lines of code had been called during testing
But I think there is another way to check your problem it is called mutation testing, here are some libraries that could help you with it
https://github.com/sixty-north/cosmic-ray
https://github.com/mutpy/mutpy
And also look into property based testing libraries like https://github.com/HypothesisWorks/hypothesis/tree/master/hypothesis-python
I have a class with some #staticmethod's that are procedures, thus they do not return anything / their return type is None.
If they fail during their execution, they throw an Exception.
I want to unittest this class, but I am struggling with designing positive tests.
For negative tests this task is easy:
assertRaises(ValueError, my_static_method(*args))
assertRaises(MyCustomException, my_static_method(*args))
...but how do I create positive tests? Should I redesign my procedures to always return True after execution, so that I can use assertTrue on them?
Without seeing the actual code it is hard to guess, however I will make some assumptions:
The logic in the static methods is deterministic.
After doing some calculation on the input value there is a result
and some operation is done with this result.
python3.4 (mock has evolved and moved over the last few versions)
In order to test code one has to check that at least in the end it produces the expected results. If there is no return value then the result is usually stored or send somewhere. In this case we can check that the method that stores or sends the result is called with the expected arguments.
This can be done with the tools available in the mock package that has become part of the unittest package.
e.g. the following static method in my_package/my_module.py:
import uuid
class MyClass:
#staticmethod
def my_procedure(value):
if isinstance(value, str):
prefix = 'string'
else:
prefix = 'other'
with open('/tmp/%s_%s' % (prefix, uuid.uuid4()), 'w') as f:
f.write(value)
In the unit test I will check the following:
open has been called.
The expected file name has been calculated.
openhas been called in write mode.
The write() method of the file handle has been called with the expected argument.
Unittest:
import unittest
from unittest.mock import patch
from my_package.my_module import MyClass
class MyClassTest(unittest.TestCase):
#patch('my_package.my_module.open', create=True)
def test_my_procedure(self, open_mock):
write_mock = open_mock.return_value.write
MyClass.my_procedure('test')
self.assertTrue(open_mock.call_count, 1)
file_name, mode = open_mock.call_args[0]
self.assertTrue(file_name.startswith('/tmp/string_'))
self.assertEqual(mode, 'w')
self.assertTrue(write_mock.called_once_with('test'))
If your methods do something, then I'm sure there should be a logic there. Let's consider this dummy example:
cool = None
def my_static_method(something):
try:
cool = int(something)
except ValueError:
# logs here
for negative test we have:
assertRaises(ValueError, my_static_method(*args))
and for possitive test we can check cool:
assertIsNotNone(cool)
So you're checking if invoking my_static_method affects on cool.
I have just begun learning Python and I got stuck (1day experience :)). Couldn't you help me with my homework?
Exercise:
We have module checkers with function is_triangle
The method signature with a documentation string:
def is_triangle(a, b, c):
"""
:param a: length of first side
:param b: length of second side
:param c: length of third side
:return: "True" if possible to create triangle with these sides. Otherwise "False"
"""
You should develop full set of tests that will verify this function.
The solution should use the nosetest library and it should be carried out in one file like:
$ Python% your_file_name% .py
What should I write in this .py file?
In this .py file you should write
import nose.tools as nt
from checkers import is_triangle
def test_is_triangle():
# Define test_a, test_b, and test_c here such that they should produce a
# return value of False.
nt.assert_false(is_triangle(test_a, test_b, test_c))
# Redefine test_a, test_b, and test_c here such that they should produce a
# return value of True.
nt.assert_true(is_triangle(test_a, test_b, test_c))
You should then run this script in one of two ways:
$ nosetests your_file_name.py
or
$ python your_file_name.py # as your instructor has requested.
The test should fail until you have a properly written is_triangle function. If you find that your initial tests are inadequate -- such that the tests pass even though is_triangle is incorrect -- add more.
I have a python module/script which does a few of these
At various nested levels inside the script I take command line inputs, validate them, apply sensible defaults
I also check if a few directories exist
The above are just two examples. I am trying to find out what is the best "strategy" to test this. What I have done is that I have constructed wrapper functions around raw_input and os.path.exists in my module and then in my test I override these two functions to take input from my array list or do some mocked behaviour. This approach has the following disadvantages
Wrapper functions just exist for the sake of testing and this pollutes the code
I have to remember to use the wrapper function in the code everytime and not just call os.path.exists or raw_input
Any brilliant suggestions?
The short answer is to monkey patch these system calls.
There are some good examples in the answer to How to display the redirected stdin in Python?
Here is a simple example for raw_input() using a lambda that throws away the prompt and returns what we want.
System Under Test
$ cat ./name_getter.py
#!/usr/bin/env python
class NameGetter(object):
def get_name(self):
self.name = raw_input('What is your name? ')
def greet(self):
print 'Hello, ', self.name, '!'
def run(self):
self.get_name()
self.greet()
if __name__ == '__main__':
ng = NameGetter()
ng.run()
$ echo Derek | ./name_getter.py
What is your name? Hello, Derek !
Test case:
$ cat ./t_name_getter.py
#!/usr/bin/env python
import unittest
import name_getter
class TestNameGetter(unittest.TestCase):
def test_get_alice(self):
name_getter.raw_input = lambda _: 'Alice'
ng = name_getter.NameGetter()
ng.get_name()
self.assertEquals(ng.name, 'Alice')
def test_get_bob(self):
name_getter.raw_input = lambda _: 'Bob'
ng = name_getter.NameGetter()
ng.get_name()
self.assertEquals(ng.name, 'Bob')
if __name__ == '__main__':
unittest.main()
$ ./t_name_getter.py -v
test_get_alice (__main__.TestNameGetter) ... ok
test_get_bob (__main__.TestNameGetter) ... ok
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
Solution1: I would do something like this beacuse it works:
def setUp(self):
self._os_path_exists = os.path.exists
os.path.exists = self.myTestExists # mock
def tearDown(self):
os.path.exists = self._os_path_exists
It is not so nice.
Solution2: Restructuring your code was not an option as you said, right?
It would make it worse to understand and unintuitive.
Johnnysweb is spot on with what you need to do, but instead of rolling your own, you can import and use mock. Mock is specifically designed for unit testing, and makes it extremely simple to do what you're trying to do. It's built-in to Python 3.3.
For example, if want to run a unit test that replaces os.path.isfile and always returns True:
try:
from unittest.mock import patch
except ImportError:
from mock import patch
class SomeTest(TestCase):
def test_blah():
with patch("os.path.isfile", lambda x: True):
self.assertTrue(some_function("input"))
This can save you a LOT of boilerplate code, and it's quite readable.
If you need something a bit more complex, for example, replacing supbroccess.check_output, you can create a simple helper function:
def _my_monkeypatch_function(li):
x,y = li[0], li[1]
if x == "Reavers":
return "Gorram"
if x == "Inora":
return "Shiny!"
if x == y:
return "The Ballad of Jayne"
def test_monkey():
with patch("subprocess.check_output", _my_monkeypatch_function):
assertEquals(subprocess.check_output(["Mudder","Mudder"]),
"The Ballad of Jayne")