46

I have some code like:

class HelloTest:

    def foo(self, msg):
        self.bar(msg.upper())

    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?

6 Answers 6

22

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.

Sign up to request clarification or add additional context in comments.

2 Comments

in line self.assertEqual(ob.msg, "SOME STRING"), where is ob coming from?
You know... I really don't know? Looks like I made a mistake a 6 years ago :-) It should be something like: self.assertEqual(mock_bar.call_args[0][0], "SOME STRING") I'll update the answer.
19

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 achieve its functionality, it should not be of our concern. We should test that foo(msg) writes msg.upper() to sys.stdout.

You can redirect sys.stdout to a string buffer and check if the content of this buffer matches what you expect.

Example:

import io
import sys
import unittest


class TestScript(unittest.TestCase):

    def setUp(self):
        self._old_stdout = sys.stdout
        sys.stdout = io.TextIOWrapper(io.BytesIO(), sys.stdout.encoding)

    def test_foo(self):
        hello_test = HelloTest()
        hello_test.foo('hello')
        sys.stdout.seek(0)
        self.assertEqual(sys.stdout.read(), 'HELLO\n')

    def tearDown(self):
        sys.stdout.close()
        sys.stdout = self._old_stdout


if __name__ == '__main__':
    unittest.main()

For a version for which sys.stdout.write() accepts both unicode and byte strings, see this answer.

Comments

10

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.

2 Comments

Well, if he is using anything below Python 3 then it's not that simple to mock print. He could mock sys.stdout, but he would have to change bar
This answer would work if the question was how to test bar, but not if we are testing foo, since foo doesn't call print. The functionality of foo is to call bar with a capitalised message. The answer from @aychedee is correct since it only tests the functionality of foo, not bar.
8

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()

Comments

5

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)

1 Comment

This tests something. It's not necessarily what needs to be tested, though.
4

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`

1 Comment

Good solution for doing unit test on function with output. But what I care most is how to test def foo(self, msg), since not all function do something with stdout

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.