Test for instantiation of a mocked class in Python?

Question:

I want to make my tests "real" unittests in a strict meaning. There shouldn’t be any dependencies. All dependencies should be mocked.

The class Bar in module bar need to be tested. But in some situations it will have a member of type Foo from module foo. The goal is that the unittest does not have to import foo or foo.Foo.

My mocking seems to work. But I’m not sure how to test if Bar() now does have tried to instantiate foo.Foo(). My assert_called() in the below example failed with Expected 'mock' to have been called.. And that assert also doesn’t give me all information’s I want.

This is the test code:

#!/usr/bin/env python3
import unittest
from unittest import mock

import bar


# The goal ist to test "Bar" wihtout importing "foo.Foo" as an extra dependency
class MyTest(unittest.TestCase):
    def test_bar(self):
        mock_foo = mock.Mock()
        bar.foo = mock_foo

        b = bar.Bar(7)

        print('')
        print(f'{mock_foo=}')
        print(f'{b.y=}')

        # Of course this doesn't work, because "foo" is not imported
        # self.assertIsInstance(b.y, foo.Foo)

        print(mock_foo.__dict__)
        mock_foo.assert_called()  # failes

    def test_int(self):
        b = bar.Bar(8)
        self.assertIsInstance(b.y, int)


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

Here comes module bar:

import foo


class Bar:
    def __init__(self, y):
        if y == 7:
            self.y = foo.Foo(7)
        else:
            self.y = y

And this is module foo:

class Foo:
    def __init__(self, x):
        self.x = x
Asked By: buhtz

||

Answers:

When you’re mocking, you should really be using mock.patch() to do your patching.

consider, instead:

#!/usr/bin/env python3

import sys
import unittest
from unittest import mock

import bar


class MyTest(unittest.TestCase):

    @mock.patch('foo.Foo')
    def test_bar(self, foo_mock):

        b = bar.Bar(7)

        # This _does_ work, because the patch decorator gives us the patched
        # mock.
        # this is something you should _never_ include in a proper unit test,
        # but here to demonstrate the patching
        self.assertIs(foo_mock, sys.modules['foo'].Foo)

        foo_mock.assert_called_once_with(7)

    def test_no_foo(self):
        for test_val in [0, True, False, None, 13, 666]:
            with self.subTest(test_val=test_val):
                b = bar.Bar(test_val)

                self.assertEqual(b.y, test_val)


if __name__ == '__main__':
    unittest.main()
Answered By: HunnyBear
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.