Mock superclass __init__ method or superclass in general for testing

I want to check out a Python class I have written that looks like this:

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

defined by 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 to check specific_method

? Besides GenericClass.initialize

, should I make fun of GenericClass

in general GenericClass.__init__

, super

or GenericClass.a

and GenericClass.b

? Or is my approach to the problem completely wrong?

Please use it mock

as a mocking library and pytest

as a test environment. The solution must be Python2.6 + compatible.

Thanks in advance.

+3


source to share


1 answer


It's a bit tricky to guess the best approach from the synthetic example. Whenever possible, it is best to use only the required layouts and most possible real objects. But this is a compromise game, and when it is difficult to create a real object, and the dependencies are deep, and distorted layouts can be a very powerful tool. Plus, bullying gives you a variety of feeling points where your test can play. The fee is worth paying: sometimes with a breadboard, you can hide bugs that can only be raised with integration tests.

IMHO for your case, your best bet is to just mock a

and set the return value fetch

. You can also test the calla.initialize()

If your goal is just mock a.initialize()

(and off course a.fetch()

, which I think doesn't make sense without initialize()

); best is just patch

a

an object.



In any case, the last test is about fixing the superclass. In your case, it's a little difficult, and you have to pay __init__

to insert an attribute a

, b

. If you are worried about what GenericClass

to do in init, you have to patch it directly with the __init__

: patch method this class is useless because its reference is already in the definition SpecificClass

(it is resolved super

, not GenericClass.__init__(...)

>). Perhaps in real code you need to struggle with the read-only property for a

and b

: again, if you can use a real object and just fix as little method / object as possible, this is the best choice. I love the framework mock

, but in some cases this is not the best deal.

One more thing: I used patch.multiple

to have a more compact example, but fixing the method with two patch.object

does the same job.

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()

      

+3


source







All Articles