Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overriding Python mock's patch decorator

Tags:

I have a Python TestCase class where all test methods, except one, need to patch an object the same way. The other method need some other behavior from the same object. I'm using mock, so I did:

@mock.patch('method_to_patch', mock.Mock(return_value=1)) class Tests(TestCase):      @mock.patch('method_to_patch', mock.Mock(return_value=2))     def test_override(self):          (....) 

But that's not working. When test_override is run, it still calls the patched behavior from the class decorator.

After a lot of debugging, I found out that during the TestSuite build, the @patch around test_override is being called before the one around Tests, and since mock apply the patches in order, the class decorator is overriding the method decorator.

Is this order correct? I was expecting the opposite and I'm not really sure how to override patching... Maybe with a with statement?

like image 230
Gabe Avatar asked Oct 05 '12 20:10

Gabe


People also ask

How do you repair a decorator in Python?

To patch a decorator, you need to either import or reload the module which uses that decorator after patching it OR redefine the module's reference to that decorator altogether. Decorators are applied at the time that a module is imported.

What is the difference between mock and MagicMock?

So what is the difference between them? MagicMock is a subclass of Mock . It contains all magic methods pre-created and ready to use (e.g. __str__ , __len__ , etc.). Therefore, you should use MagicMock when you need magic methods, and Mock if you don't need them.

What is MagicMock Python?

MagicMock. MagicMock objects provide a simple mocking interface that allows you to set the return value or other behavior of the function or object creation call that you patched. This allows you to fully define the behavior of the call and avoid creating real objects, which can be onerous.


1 Answers

Well, turns out that a good night sleep and a cold shower made me rethink the whole issue. I'm still very new to the concept of mocking, so it still hasn't sunk in quite right.

The thing is, there's no need to override the patch to a mocked object. It's a mocked object and that means I can make it do anything. So my first try was:

@mock.patch('method_to_patch', mock.Mock(return_value=1)) class Tests(TestCase):      def test_override(self):          method_to_patch.return_value = 2          (....) 

That worked, but had the side effect of changing the return value for all following tests. So then I tried:

@mock.patch('method_to_patch', mock.Mock(return_value=1)) class Tests(TestCase):      def test_override(self):          method_to_patch.return_value = 2          (....)          method_to_patch.return_value = 1 

And it worked like a charm. But seemed like too much code. So then I went the down the road of context management, like this:

@mock.patch('method_to_patch', mock.Mock(return_value=1)) class Tests(TestCase):      def test_override(self):          with mock.patch('method_to_patch', mock.Mock(return_value=2):              (....) 

I think it seems clearer and more concise.

About the order in which the patch decorators were being applied, it's actually the correct order. Just like stacked decorators are applied from the bottom up, a method decorator is supposed to be called before the class decorator. I guess it makes sense, I was just expecting the opposite behavior.

Anyway, I hope this help some poor newbie soul like mine in the future.

like image 157
Gabe Avatar answered Oct 21 '22 17:10

Gabe