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.
Is little bit hard to guess the best approach from a synthetic example. When is possible use just the needed mocks and the most of possible real object is the best. But it is a trade off game and when create real object is hard and dependencies are deep and twisted mocks can be very powerful tool. Moreover mocks give to you lot of sense points where your test can play. There is a price to pay: sometimes by use mock you can hide bugs that can be raised just by integration tests.
IMHO for your case the best is just mock a
and set fetch
's return value. You can also test a.initialize()
call
If your target is just mock a.initialize()
(and off course a.fetch()
that I guess have 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()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With