如何使用补丁装饰器修补方法而不是将其与上下文管理器一起使用

时间:2014-11-20 08:18:57

标签: python unit-testing

我有以下Python单元测试用例。

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)

我可以使用@patch装饰器而不是上下文管理器来模拟add_dns_analysis方法吗?我应该何时使用其中任何一个。

2 个答案:

答案 0 :(得分:2)

在您的情况下,您可以使用装饰器和上下文,甚至patch而不是patch.object。例如,您的测试可以重写为

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

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

修改 我用问题写了那个答案何时使用...... 而不是如何使用... 。所以下一部分是关于何时使用补丁装饰器修补方法而不是将其与上下文管理器一起使用

但一般来说,装饰者可以获得比上下文版本更清晰的代码。例如,如果需要修补更多对象,装饰器版本看起来像

@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):
    ...

但上下文版本如下:

@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:
                ....

我认为在那种情况下,装饰者的版本真的更好。

patch装饰者很强大,但不是银弹。例如,如果在测试中你需要在补丁中配置依赖于其他对象或类的东西,你就不能使用装饰器:

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

您必须使用with而不是patch的最后一个示例是,当您在同一测试中需要更多同一对象的一个​​补丁时,或者您需要修补和未修补的版本时:

 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

最后,您可以通过直接使用enter()对象的exit() patch方法获得完整的细粒度,但我认为这个答案中的范围有点超出范围。

答案 1 :(得分:0)

是的,您可以使用@patch装饰器。在我看来,两个决定之间没有太大的区别。