Question

I am trying to get a hang of mocking objects, and seem to by confused by something very basic. I am trying to mock object MyClass and then unit test one of its methods. here is my code:

import mock
import unittest

class MyClass(object):
    def __init__(self, a):
        self.a = a
    def add_two(self):
        return self.a + 2

class TestMyClass(unittest.TestCase):
    @mock.patch('__main__.MyClass')
    def test_add_two(self, dummy_mock):
        m_my_class = mock.Mock()
        m_my_class.a = 10
        result = m_my_class.add_two() # I would expect the result to be 12
        import ipdb;ipdb.set_trace()
        self.assert_equal(result, 12)

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

In m_my_class.a = 10 I set value of a to to and then in m_my_class.add_two() I add a two, shouldn't I get 12? However, result is:

     16         import ipdb;ipdb.set_trace()
---> 17         self.assert_equal(result, 12)
     18 

ipdb> result
<Mock name='mock.add_two()' id='18379792'>

What am I missing?

Since I am passing the location of the class via decorator to the test method @mock.patch('__main__.MyClass'), shouldn't mocked have all the methods? Because if not, then why does it matter what class we include in the decorator?

Edit:

When I run this code, I still get the same thing.

class TestMyClass(unittest.TestCase):
    @mock.patch('__main__.MyClass')
    def test_add_two(self, dummy_mock):
        dummy_mock.a = 10
        result = dummy_mock.add_two()
        import ipdb;ipdb.set_trace()
        self.assert_equal(result, 12)

Result:

ipdb> result
<MagicMock name='MyClass.add_two()' id='38647312'>
Was it helpful?

Solution

Patching the class is almost certainly not what you want to do here. For example, if you changed your test method to this:

class TestMyClass(unittest.TestCase):
    @mock.patch('__main__.MyClass')
    def test_add_two(self, dummy_mock):
        m_my_class = MyClass(5)
        print m_my_class

You will find that rather than getting what you expect:

<__main__.MyClass object at 0x0000000002C53E48>

You'll get this:

<MagicMock name='MyClass()' id='46477888'>

Patching the class in the method means that any time within the method that something tries to instantiate the class it will instead get a mock object. In your particular case, calling MyClass(5) will return the same mock object as calling dummy_mock would. This allows you to set up the mock object so that when you go to test your code the mock object will behave as you want it to.

You really would only use this for dependencies, not the class you are testing. So for example, if your class retrieved data from SQL, you'd patch the SQL class to give your class a mock instead of the normal SQL connection. This allows you to test a class in isolation, without having to worry about externals (like the SQL) getting in the way.

In your example though, you seem to be trying to test a method in isolation. A way you could do that is to extract the function from the method and use that. In code:

def test_add_two(self):
    test_mock = mock.Mock()
    test_mock.add_two = MyClass.add_two.__func__
    test_mock.a = 10
    result = test_mock.add_two(test_mock)
    self.assert_equal(result, 12)

Normally you wouldn't have to pass in an argument for the self argument, but as here we've pulled out the function you do need to pass in the self argument. If you want you can turn the function into a method bound to your mock object, like this:

import types
...
    test_mock.add_two = types.MethodType(MyClass.add_two.__func__, test_mock, test_mock.__class__)
    result = test_mock.add_two()

If the function you are testing calls any other methods, you'll need to either do this or mock it out for each of the methods.

I would advise against using this strategy too much, in part because of needing to take care of methods that the tested method calls. Of course, being able to mock out the methods it depends on may be exactly what you're trying to do. In any case, it requires the unit tests to have a pretty deep understanding of how the method works, and not just of what it is supposed to produce. That makes the test very fragile, making it so that you are likely to see the test break far more often than the method you are testing.

OTHER TIPS

Why are you mocking your SUT? This is generally something that you should avoid doing because it introduces the risk that you will not be testing what you think you're testing.

The point of a unit test is to verify the behavior of a unit in complete isolation. One unit typically collaborates with other units in order to provide some useful functionality. Test doubles (mocks, fakes, etc.) are the tools used to achieve this isolation. In a unit test, collaborators of the SUT are replaced with test doubles in order to minimize the number of moving parts.

Your SUT, MyClass, doesn't appear to have any collaborators. As such, no test doubles are necessary to test this unit in isolation (it's already self-contained). This allows you to greatly simplify your unit test:

import mock
import unittest

class MyClass(object):
    def __init__(self, a):
        self.a = a
    def add_two(self):
        return self.a + 2

class TestMyClass(unittest.TestCase):
    def test_add_two(self):
        m_my_class = MyClass()
        m_my_class.a = 10
        result = m_my_class.add_two() # I would expect the result to be 12
        import ipdb;ipdb.set_trace()
        self.assert_equal(result, 12)

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

Edit: The following code demonstrates how a mock object might be used. (Disclaimer: I don't normally work in Python, so my code is probably not very idiomatic. Hopefully the core point still makes sense, though.)

In this example, MyClass adds a value provided by a collaborator, instead of a hardcore value (2).

import mock
import unittest

class MyClass(object):
    def __init__(self, a, get_value):
        self.a = a
        self.get_value = get_value
    def add_value(self):
        return self.a + self.get_value()

class TestMyClass(unittest.TestCase):
    def test_add_value(self):
        m_test_value = 42
        m_test_a = 10
        m_my_class = MyClass()
        m_get_test_value = mock.Mock(return_value=m_test_value)
        m_my_class.a = test_a

        result = m_my_class.add_value()
        import ipdb;ipdb.set_trace()
        self.assert_equal(result, m_test_a + m_test_value)

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

The above example uses a mock object, instead of patching a class. Here is a pretty good explanation of the difference:

Mocking a class: Mock() or patch()?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top