geujv
geujv

Reputation: 223

Python-mock: how to test if super() was called

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

Answers (2)

thehale
thehale

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

Malina Kirn
Malina Kirn

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

Related Questions