3

I have a class that initializes an instance variable in a method other than __init__. That instance variable is further referred to in the __init__ method.

In the code below, ProductionClass is the class I am testing. In ProductionClass, __init__ makes a call to method a_method. Method a_method initializes an instance variable inst_var_2. inst_var_2 is used by __init__ to set another instance variable inst_var_3.

class LibraryClass(object):
    def __init__(self):
        self.some_attrib = "some_attrib_value"

class ProductionClass(object):
    def __init__(self, arg_one):
        self.inst_var_1 = arg_one
        self.a_method()
        self.inst_var_3 = self.inst_var_2.some_attrib

    def a_method(self):
        self.inst_var_2 = LibraryClass()


import unittest
from unittest.mock import patch, MagicMock

class ProdTestCase(unittest.TestCase):

    @patch.object(ProductionClass, 'a_method')
    def test_init_method(self, mock_method):

        instance = ProductionClass(1234)
        self.assertEqual(1234, instance.inst_var_1)
        mock_method.assert_called_once_with()

I am trying to unit test the __init__ method of ProductionClass and I am unable to create an instance of the ProductionClass, due to missing instance variable inst_var_2. Here is the error message:

Traceback (most recent call last):

  ...

  File "/...     production_class.py", line 9, in __init__
    self.inst_var_3 = self.inst_var_2.some_attrib
AttributeError: 'ProductionClass' object has no attribute 'inst_var_2'

My question, is it possible to unit test the __init__ method while I am mocking the a_method. I want to mock a_method because I don't want the method to instantiate LibraryClass.

Kalyan Vedala
  • 1,049
  • 2
  • 17
  • 33

1 Answers1

2

Your question is essentially the same as: python mock - patching a method without obstructing implementation.

Since you mocked out a_method, it doesn't get to set the self.inst_var_2. Therefore the __init__ can not succeed. You will have to account for this in the test, however, you can still assert that the mock was called:

@patch.object(ProductionClass, 'a_method')
def test_init_method(self, mock_method):
    with self.assertRaises(AttributeError):
        ProductionClass(1234):
    mock_method.assert_called_once_with()

I want to mock a_method because I don't want the method to instantiate LibraryClass.

It will be better to patch out your_module.LibraryClass, not a_method.

wim
  • 338,267
  • 99
  • 616
  • 750
  • Thanks for suggesting a way to call `assert_called_once_with` even if instantiation of `ProductionClass` fails. Also your suggestion that I mock out `LibraryClass` instead of `a_method` is very helpful. Sometimes I tend to get stuck in taking a "pure" approach when I am mocking out all method calls form the method I am calling... – Kalyan Vedala Aug 15 '18 at 18:33
  • The link to your question is very helpful too. I will try to apply some of the techniques outlined there in my tests. – Kalyan Vedala Aug 15 '18 at 18:36