In python, how to do unit test on a function without return value?
Question:
I have some code like:
class HelloTest(object):
def foo(self, msg):
MSG = msg.upper()
self.bar(MSG)
def bar(self, MSG):
print MSG
Using the unittest
standard library, we can use methods like assertEqual
, assertTrue
etc. to verify what is returned from a function.
Since foo
does not return a value explicitly, how can I test that it does the right thing?
Answers:
In this particular case, I would mock print, then then use the mock in my assertion.
In Python, you will use the Mock package to mock.
In Python 3, you can tell print
where to print to:
print(*objects, sep=’ ‘, end=’n’, file=sys.stdout, flush=False)
So add an optional argument:
def bar(self, MSG, file=sys.stdout):
print(MSG, file=file)
In normal usage, it will print to stdout, but for unit tests you can pass your own file.
In Python 2 it’s a bit messier, but you can redirect stdout to a StringIO buffer:
import StringIO
import sys
out = StringIO.StringIO()
sys.stdout = out
# run unit tests
sys.stdout = sys.__stdout__
# check the contents of `out`
your code can be as given below which does same task as above:
class HelloTest(object):
def foo(self, msg):
self.msg = msg.upper()
self.bar()
def bar(self):
print self.msg
Unit test is :
from hello import HelloTest
import unittest
class TestFoo(unittest.TestCase):
def test_foo_case(self):
msg = "test"
ob = HelloTest()
ob.foo(msg)
expected = "TEST"
self.assertEqual(ob.msg, expected)
if __name__ == '__main__':
unittest.main(exit=False)
As another answer mentioned, you can use the Python mock library to make assertions about calls to functions/methods
from mock import patch
from my_module import HelloTest
import unittest
class TestFoo(unittest.TestCase):
@patch('hello.HelloTest.bar')
def test_foo_case(self, mock_bar):
ht = HelloTest()
ht.foo("some string")
self.assertTrue(mock_bar.called)
self.assertEqual(mock_bar.call_args[0][0], "SOME STRING")
This patches out the bar
method on HelloTest and replaces it with a mock object that records calls against it.
Mocking is a bit of a rabbit hole. Only do it when you absolutely have to because it does make your tests brittle. You’ll never notice an API change for a mocked object for instance.
Thanks to @Jordan ‘s introduction, I code this and think it is a workable unit test for HelloTest.foo
from mock import Mock
import unittest
class HelloTestTestCase(unittest.TestCase):
def setUp(self):
self.hello_test = HelloTest()
def tearDown(self):
pass
def test_foo(self):
msg = 'hello'
expected_bar_arg = 'HELLO'
self.hello_test.bar = Mock()
self.hello_test.foo(msg)
self.hello_test.bar.assert_called_once_with(expected_bar_arg)
if __name__ == '__main__':
unittest.main()
I don’t quite understand why everybody wants to check that foo calls bar.
Foo has some functionality and this functionality needs to be tested. If foo is using bar to do this should not be of my concern.
The desired result is that after foo(msg)
is called, is that msg.upper()
is sent to stdout.
You can redirect stdout to a string buffer and check if the content of this string buffer matches what you expect.
Example:
import sys
import unittest
from io import TextIOWrapper, BytesIO
class TestScript(unittest.TestCase):
def setUp(self):
self._old_stdout = sys.stdout
sys.stdout = TextIOWrapper(BytesIO(), sys.stdout.encoding)
def _output(self):
self._stdout.seek(0)
return self._stdout.read()
def test_foo(self):
hello_test = HelloTest()
hello_test.foo("blub")
self.assertEqual(self._output(), "BLUB")
def tearDown(self):
sys.stdout = self._old_stdout
self._stdout.close()
You can also do that for stdin (and write to stdin to mock some input) and you can subclass TestIOWrapper if you need anything special to be done, like allowing non-unicode text to be sent to sys.stdout
without using sys.stdout.buffer
(Python 2 vs. Python 3).
There is an example for that in this SO answer.
When you (still) use Python 2 only, then using StringIO
might be better than using the io module.
I have some code like:
class HelloTest(object):
def foo(self, msg):
MSG = msg.upper()
self.bar(MSG)
def bar(self, MSG):
print MSG
Using the unittest
standard library, we can use methods like assertEqual
, assertTrue
etc. to verify what is returned from a function.
Since foo
does not return a value explicitly, how can I test that it does the right thing?
In this particular case, I would mock print, then then use the mock in my assertion.
In Python, you will use the Mock package to mock.
In Python 3, you can tell print
where to print to:
print(*objects, sep=’ ‘, end=’n’, file=sys.stdout, flush=False)
So add an optional argument:
def bar(self, MSG, file=sys.stdout):
print(MSG, file=file)
In normal usage, it will print to stdout, but for unit tests you can pass your own file.
In Python 2 it’s a bit messier, but you can redirect stdout to a StringIO buffer:
import StringIO
import sys
out = StringIO.StringIO()
sys.stdout = out
# run unit tests
sys.stdout = sys.__stdout__
# check the contents of `out`
your code can be as given below which does same task as above:
class HelloTest(object):
def foo(self, msg):
self.msg = msg.upper()
self.bar()
def bar(self):
print self.msg
Unit test is :
from hello import HelloTest
import unittest
class TestFoo(unittest.TestCase):
def test_foo_case(self):
msg = "test"
ob = HelloTest()
ob.foo(msg)
expected = "TEST"
self.assertEqual(ob.msg, expected)
if __name__ == '__main__':
unittest.main(exit=False)
As another answer mentioned, you can use the Python mock library to make assertions about calls to functions/methods
from mock import patch
from my_module import HelloTest
import unittest
class TestFoo(unittest.TestCase):
@patch('hello.HelloTest.bar')
def test_foo_case(self, mock_bar):
ht = HelloTest()
ht.foo("some string")
self.assertTrue(mock_bar.called)
self.assertEqual(mock_bar.call_args[0][0], "SOME STRING")
This patches out the bar
method on HelloTest and replaces it with a mock object that records calls against it.
Mocking is a bit of a rabbit hole. Only do it when you absolutely have to because it does make your tests brittle. You’ll never notice an API change for a mocked object for instance.
Thanks to @Jordan ‘s introduction, I code this and think it is a workable unit test for HelloTest.foo
from mock import Mock
import unittest
class HelloTestTestCase(unittest.TestCase):
def setUp(self):
self.hello_test = HelloTest()
def tearDown(self):
pass
def test_foo(self):
msg = 'hello'
expected_bar_arg = 'HELLO'
self.hello_test.bar = Mock()
self.hello_test.foo(msg)
self.hello_test.bar.assert_called_once_with(expected_bar_arg)
if __name__ == '__main__':
unittest.main()
I don’t quite understand why everybody wants to check that foo calls bar.
Foo has some functionality and this functionality needs to be tested. If foo is using bar to do this should not be of my concern.
The desired result is that after foo(msg)
is called, is that msg.upper()
is sent to stdout.
You can redirect stdout to a string buffer and check if the content of this string buffer matches what you expect.
Example:
import sys
import unittest
from io import TextIOWrapper, BytesIO
class TestScript(unittest.TestCase):
def setUp(self):
self._old_stdout = sys.stdout
sys.stdout = TextIOWrapper(BytesIO(), sys.stdout.encoding)
def _output(self):
self._stdout.seek(0)
return self._stdout.read()
def test_foo(self):
hello_test = HelloTest()
hello_test.foo("blub")
self.assertEqual(self._output(), "BLUB")
def tearDown(self):
sys.stdout = self._old_stdout
self._stdout.close()
You can also do that for stdin (and write to stdin to mock some input) and you can subclass TestIOWrapper if you need anything special to be done, like allowing non-unicode text to be sent to sys.stdout
without using sys.stdout.buffer
(Python 2 vs. Python 3).
There is an example for that in this SO answer.
When you (still) use Python 2 only, then using StringIO
might be better than using the io module.