简体   繁体   中英

How to use the patch decorator to patch a method instead of using it with context manager

I have the following Python unit test case.

import json
import rules
from rules import RULES
import models
from django.test import TestCase
from mock import patch

class RulesTest(TestCase):

    request_length = 484
    url = "http://www.ndtv.com"

    def setUp(self):
        har_data = open('/Users/rokumar/SiteAnalysisGit/Src/hct/exportfiles/test.har')
        self.data = json.load(har_data)
        self.rule = models.Rule(name=RULES.DNS,user=None,
            threshold=None)
        self.rule.save()

    def tearDown(self):
        self.rule.delete()

    def test_parse_har(self):
        with patch.object(rules, 'add_dns_analysis', return_value=None) as \
        dns_mock: 
            dns_mock.add_dns_analysis('test result', 'test url')
            result = rules.parse_har(self.data,[self.rule],RulesTest.url)
            self.assertEqual(result[RULES.TOTAL_REQUESTS], 484)

Can I mock the add_dns_analysis method using @patch decorator instead of context manager and when should I use either of them.

In your case you can use either decorator and context and even patch instead of patch.object . For instance your test could be rewrite as

@patch.object(rules, 'add_dns_analysis', return_value=None)
def test_parse_har(self,dns_mock):
    dns_mock.add_dns_analysis('test result', 'test url')
    result = rules.parse_har(self.data,[self.rule],RulesTest.url)
    self.assertEqual(result[RULES.TOTAL_REQUESTS], 484)

or

@patch('rules.add_dns_analysis', return_value=None)
def test_parse_har(self,dns_mock):
    dns_mock.add_dns_analysis('test result', 'test url')
    result = rules.parse_har(self.data,[self.rule],RulesTest.url)
    self.assertEqual(result[RULES.TOTAL_REQUESTS], 484)

EDIT I wrote that answer by thing the question was When to use... instead of How to use... . So the next part is about When to use the patch decorator to patch a method instead of using it with context manager .

But in general by decorator you can obtain cleaner code than the context version. For instance if need to patch more object the decorator version looks like

@patch('amodule.A.a_method')
@patch('amodule.A.b_method')
@patch('amodule.B.c_method')
def test_mytest(self,bc_mock,ab_mock,aa_mock):
    ...

but the context version looks like:

@patch('amodule.A.a_method')
@patch('amodule.A.b_method')
@patch('amodule.B.c_method')
def test_mytest(self):
    with patch('amodule.A.a_method') as aa_mock:
        with patch('amodule.A.b_method') as ab_mock:
            with patch('amodule.B.c_method') as bc_mock:
                ....

I think that in that case decorator's version is real better.

patch decorator is powerful but not a silver bullet. For instance if in your test you need configure something in your patch that depend from other object or your class you can't use decorator:

def mytest(self):
    foo = Foo()
    with patch('amodule.A.a_method', return_value=self.a_return, my_property=foo) as m:
        ...

The last example where you have to use with instead of patch is when you need more one patch of the same object in the same test or you need both patched and unpatched version:

 def mytest(self):
    with patch('amodule.A.a_method', return_value="first") as m:
         ....
    with patch('amodule.A.a_method', return_value="second") as m:
         ....
    amodule.A.a_method() #original

Finally you can obtain a complete fine grain by a direct use of enter() exit() methods of the patch object but I think that is a little bit out of scope in that answer.

Yes, you could use @patch decorator. In my opinion, there is no big difference between both decisions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM