I want to test a Python class I wrote, which is like the following:
from external_library import GenericClass
class SpecificClass(GenericClass):
def __init__(self, a, b, c):
super(SpecificClass, self).__init__(a, b)
self.c = c
def specific_method(self, d):
self.initialize()
return d + self.c + self.b + self.a.fetch()
GenericClass
is defined by an external library (external_library
):
class GenericClass(object):
def __init__(self, a, b):
self.a = a
self.b = b + "append"
def initialize(self):
# it might be an expensive operation
self.a.initialize()
def specific_method(self, d):
raise NotImplementedError
How should I test specific_method
? Other than GenericClass.initialize
, should I mock out GenericClass
as a whole, GenericClass.__init__
, super
or GenericClass.a
and GenericClass.b
? Or my approach to the problem is totally wrong?
Please use mock
as mocking library and pytest
as test framework. The solution is required to be Python2.6+ compatible.
Thanks in advance.
It's a little bit hard to infer what the best approach might be from a synthetic example. When possible, use just the needed mocks, since using real objects as much as possible is the best approach. But it is a trade off game, and when creating real objects is difficult and dependencies are deep, then twisted mocks can be very a powerful tool. Moreover mocks can give you a better sense points where your test can play. There is a price to pay: sometimes by using mock you can hide bugs that can be raised only by integration tests.
IMHO for your case the best approach is to just mock a
and set fetch
's return value. You can also test the call to a.initialize()
.
If your target is to just mock a.initialize()
(and of course a.fetch()
since I assume it makes no sense without initialize()
); the best thing is just patch
a
object.
Anyway last test is about patching the superclass. In your case that is little tricky you must both patch __init__
and inject a
, b
attribute. If you are worry about what GenericClass
do in init you must patch it directly __init__
method: patch the class is useless because its reference is already in SpecificClass
definition (it is resolved by super
and not by GenericClass.__init__(...)
). Maybe in the real code you should fight against read only property for a
and b
: again if you can use real object and just patch less method/object as possible is the best choice. I love mock
framework but some times use it is not the best deal.
One more thing: I used patch.multiple
just to have a more compact example, but patch the method by two patch.object
do the same work.
import unittest
from mock import Mock, patch, DEFAULT, PropertyMock
from specific import SpecificClass
class A():
def initialize(self):
raise Exception("Too slow for tests!!!!")
def fetch(self):
return "INVALID"
class MyTestCase(unittest.TestCase):
def test_specific_method(self):
a = A()
with patch.multiple(a, initialize=DEFAULT, fetch=DEFAULT) as mocks:
mock_initialize, mock_fetch = mocks["initialize"], mocks["fetch"]
mock_fetch.return_value = "a"
sc = SpecificClass(a, "b", "c")
self.assertEqual("dcbappenda", sc.specific_method("d"))
mock_initialize.assert_called_with()
def test_sanity_check(self):
a = A()
sc = SpecificClass(a, "b", "c")
self.assertRaises(Exception, sc.specific_method, "d")
#You can patch it in your module if it is imported by from .. as ..
@patch("specific.GenericClass.__init__", autospec=True, return_value=None)
def test_super_class(self, mock_init):
sc = SpecificClass("a", "b", "c")
mock_init.assert_called_with(sc, "a", "b")
#Inject a and b
sc.a = Mock()
sc.b = "b"
#configure a
sc.a.fetch.return_value = "a"
self.assertEqual("dcba", sc.specific_method("d"))
sc.a.initialize.assert_called_with()