Reputation: 223
I have the following structure:
class A(Object):
def method(self):
return 'a'
class B(A):
def __init__(self, test):
self.test = test
def method(self):
if self.test:
return super(A, self).method(self)
else:
return 'b'
What I want to do is write a test-case which would test that if self.test is true then the super function was called and class A's method function was called.
How I can achieve this? What should I mock?
Advanced Q: What about when Class A and B are in a separate module, and they have the same name. So instead of class B I would write: class A(module1.A): does that change the mocking?
Upvotes: 10
Views: 8189
Reputation: 1756
To build off of @Malina's excellent answer, if you can't patch the method on module A (e.g. A is in a third party library, part of a nasty legacy system, dynamically created at runtime, etc.) you can still test this functionality by creating another method on class B explicitly for the purpose of testing.
Revised Class B
import A
class B(A):
def __init__(self, test):
self.test = test
def method(self):
if self.test:
self._delegate_to_A() # Move the super call to a separate method
else:
return 'b'
def _delegate_to_A(self)
return super(A, self).method(self)
Revised Test Code for Class B
import mock, unittest
class TestB(unittest.TestCase):
# Patch B's `delegate` method instead of A
@mock.patch("B._delegate_to_A")
def test_super_method(self, mock_super):
B(True).method()
self.assertTrue(mock_super.called)
@mock.patch("B._delegate_to_A")
def test_super_method(self, mock_super):
B(False).method()
self.assertFalse(mock_super.called)
Upvotes: 0
Reputation: 2253
As @MartijnPieters points out, testing that super is called is typically a test of an implementation detail, rather than a test of a contract. Nonetheless, testing the specific implementation may still be desirable -- or the parent class may have a contract requiring the call to super. To test that super is called, use a mock, as mentioned by @mgilson. The answer in detail:
import mock, unittest
class TestB(unittest.TestCase):
@mock.patch("A.method")
def test_super_method(self, mock_super):
B(True).method()
self.assertTrue(mock_super.called)
@mock.patch("A.method")
def test_super_method(self, mock_super):
B(False).method()
self.assertFalse(mock_super.called)
You'll need to specify the full namespace in the patch. E.g., @mock.patch("module.submodule.A.method")
. This can be done for any method in A, including __init__
; syntax is exactly the same.
Upvotes: 12