python模拟在类中使用的全局函数

时间:2015-08-13 09:30:35

标签: python unit-testing mocking

我似乎无法理解Python中的嘲笑。我有一个全球功能:

a.py:

def has_permission(args):
    ret_val = ...get-true-or-false...
    return ret_val

b.py:

class MySerializer(HyperlinkedModelSerializer):

     def get_fields():
         fields = super().get_fields()
         for f in :
             if has_permission(...):
                 ret_val[f.name] = fields[f]
         return ret_val

c.py:

class CountrySerializer(MySerializer):
    class Meta:
        model = Country

问题:现在我要测试c.py,但我想模拟a.py中定义的 has_permission 函数,但是在get_fields中调用 - 在b.py中定义的MySerializer类的方法...我该怎么做?

我尝试过这样的事情:

@patch('b.MySerializer.has_permission')

@patch('b.MySerializer.get_fields.has_permission')

@patch('a.has_permission')

但我尝试的所有内容都不起作用且has_permission仍然执行,或者python抱怨它无法找到属性'has_permission'

完成修补:

test.py

class TestSerializerFields(TestCase):
    @patch(... the above examples....)
    def test_my_country_serializer():
        s = CountrySerializer()
        self..assertTrue(issubclass(my_serializer_fields.MyCharField, type(s.get_fields()['field1'])))

2 个答案:

答案 0 :(得分:8)

您需要修补add (T object)模块中的全局:

b

因为那是您的代码所在的位置。

另请参阅@patch('b.has_permission') 文档的Where to patch section

答案 1 :(得分:0)

您需要在测试运行时修补存在的方法。如果您尝试在测试代码已导入它之后修改定义它的方法,那么该修补程序将不起作用。在@patch(...)执行时,测试中的测试代码已经将全局方法抓到了自己的模块中。

以下是一个例子:

app/util/config.py

# This is the global method we want to mock
def is_search_enabled():
    return True

app/service/searcher.py

# Here is where that global method will be imported 
#  when this file is first imported
from app.util.config import is_search_enabled

class Searcher:
    def __init__(self, api_service):
        self._api_service = api_service

    def search(self):
        if not is_search_enabled():
            return None
        return self._api_service.perform_request('/search')

test/service/test_searcher.py

from unittest.mock import patch, Mock
# The next line will cause the imports of `searcher.py` to execute...
from app.service.searcher import Searcher
# At this point, searcher.py has imported is_search_enabled into its module.
# If you later try and patch the method at its definition 
#  (app.util.config.is_search_enabled), it will have no effect because 
#  searcher.py won't look there again.

class MockApiService:
    pass

class TestSearcher:

    # By the time this executes, `is_search_enabled` has already been
    #  imported into `app.service.searcher`.  So that is where we must
    #  patch it.
    @patch('app.service.searcher.is_search_enabled')
    def test_no_search_when_disabled(self, mock_is_search_enabled):
        mock_is_search_enabled.return_value = False
        mock_api_service = MockApiService()
        mock_api_service.perform_request = Mock()
        searcher = Searcher(mock_api_service)

        results = searcher.search()

        assert results is None
        mock_api_service.perform_request.assert_not_called()

    # (For completeness' sake, make sure the code actually works when search is enabled...)
    def test_search(self):
        mock_api_service = MockApiService()
        mock_api_service.perform_request = mock_perform_request = Mock()
        searcher = Searcher(mock_api_service)
        expected_results = [1, 2, 3]
        mock_perform_request.return_value = expected_results

        actual_results = searcher.search()

        assert actual_results == expected_results
        mock_api_service.perform_request.assert_called_once_with('/search')