Python mock: mocking base class for inheritance

I am testing a class that inherits from another very complex one, with database connection methods and a mess of dependencies. I would like to mock my base class so that I can play well with the method defined in the subclass, but at the moment when I inherit from the mocking class, the object itself turns into a layout and loses all its methods.

How can I make fun of a superclass?

More or less, the situation can be summarized in this:

import mock ClassMock = mock.MagicMock() class RealClass(ClassMock): def lol(self): print 'lol' real = RealClass() real.lol() # Does not print lol, but returns another mock print real # prints <MagicMock id='...'> 

This is a simplified case. In fact, it RealClass that RealClass extends AnotherClass , but I managed to intercept AnotherClass and replace it with a layout.

+6
source share
2 answers

This should work for you.

 import mock ClassMock = mock.MagicMock class RealClass(ClassMock): def lol(self): print 'lol' real = RealClass() real.lol() # Does not print lol, but returns another mock print real # prints <MagicMock id='...'> 

You should not pass an instance of the class just like you. mock.MagicMock is a class, so you pass it directly.

 In [2]: inspect.isclass(mock.MagicMock) Out[2]: True 
+3
source

This is something that I struggled with for a long time, but I think I finally found a solution.

As you already noticed, if you try to replace the base class with Mock, the class you are trying to test just becomes a layout that defeats your ability to test it. The solution is to make fun of only the methods of the base class, not the entire base class, but this is easier said than done: it can be quite error prone to smear each method based on the test every time.

Instead, I created a class that scans another class and assigns Mock() , which correspond to the methods of another class. Then you can use this class instead of the real base class when testing.

Here is a fake class:

 class Fake(object): """Create Mock()ed methods that match another class methods.""" @classmethod def imitate(cls, *others): for other in others: for name in other.__dict__: try: setattr(cls, name, Mock()) except (TypeError, AttributeError): pass return cls 

So, for example, you may have code like this (this is an apology that this is a little far-fetched, just assume that BaseClass and SecondClass do nontrivial work and contain a lot of methods, and you don’t even have to define them at all):

 class BaseClass: def do_expensive_calculation(self): return 5 + 5 class SecondClass: def do_second_calculation(self): return 2 * 2 class MyClass(BaseClass, SecondClass): def my_calculation(self): return self.do_expensive_calculation(), self.do_second_calculation() 

Then you can write some of these tests:

 class MyTestCase(unittest.TestCase): def setUp(self): MyClass.__bases__ = (Fake.imitate(BaseClass, SecondBase),) def test_my_methods_only(self): myclass = MyClass() self.assertEqual(myclass.my_calculation(), ( myclass.do_expensive_calculation.return_value, myclass.do_second_calculation.return_value, )) myclass.do_expensive_calculation.assert_called_once_with() myclass.do_second_calculation.assert_called_once_with() 

Thus, the methods that exist in the base classes remain available as mocks, with which you can interact, but your class does not become a layout by itself.

And I was careful to make sure that this works in both python2 and python3.

+7
source

Source: https://habr.com/ru/post/957523/


All Articles