【问题标题】:How to mock class attribute instantiated in __init__?如何模拟在__init__中实例化的类属性?
【发布时间】:2016-04-27 14:41:42
【问题描述】:

我正在尝试模拟 VKAuth 类中的“self.api.friends.get”方法:

import vk

class VKAuth(object):
    def __init__(self, access_token, user):
        self.session = vk.Session(access_token = access_token)
        self.api = vk.API(self.session)

    def follow(self):
        vk_friends = self.api.friends.get()

来自测试模块test_views.py:

from mock import patch
from ..auth_backends.vk_backend import VKAuth

class AddUsersToList(TestCase):
    def test_auth_vk(self, mock_get):
         ... etc ...
        auth_token = 'ceeecdfe0eb4bf68ceeecdfe0eb4bf68ceeecdfe0eb4bf68652530774ced6cbc8cba0'
        token = user.auth_token.key
        self.client.defaults['HTTP_AUTHORIZATION'] = 'Token {}'.format(token)
        with patch.object(accounts.auth_backends.vk_backend.VKAuth, 'api'): #point where we're mocking
            response = self.client.post(reverse('auth-social', kwargs=dict(backend='vk')), dict(access_token=auth_token), follow=True)

在基于 SNView 类的视图中对上述“auth-social”的发布调用期间创建了 VKAuth 类的实例:

class SNView(generics.GenericAPIView):
    serializer_class = serializers.AuthSocialSerializer
    permission_classes = (rest_permissions.IsAuthenticated)

    def post(self, request, backend, *args, **kwargs):
        s = self.get_serializer(data=request.DATA)

        if s.is_valid():
            auth_backends = {
                'vk': VKAuth,
                'facebook': FBAuth
            }

            if backend in auth_backends:
                auth_backend = auth_backends[backend](access_token=s.data['access_token'], user=self.request.user)

我得到一个错误:

AttributeError: <class 'accounts.auth_backends.vk_backend.VKAuth' doens't have the attribute 'api'

我应该写什么而不是当前的 patch.object 来访问 api.friends.get 并模拟它?

UPD:

更准确地说,我想要一些等价物:

    auth_token = 'ceeecdfe0eb4bf68ceeecdfe0eb4bf68ceeecdfe0eb4bf68652530774ced6cbc8cba0'
    user = User.objects.get(id = 2)
    vk_auth = VKAuth(auth_token, user)

    vk_ids=[111111,2222222,3333333,44444444]
    vk_auth.authenticate()
    vk_auth.api.friends = MagicMock(name='get', return_value=None)
    vk_auth.api.friends.get = MagicMock(name='get', return_value=vk_ids)
    data = vk_auth.follow()

但在我们通过 self.client.post() 向 django-rest-framework api 发出请求之前模拟它。

谢谢!

【问题讨论】:

    标签: python django unit-testing mocking python-mock


    【解决方案1】:

    您正在修补错误的东西。在VKAuth

    self.api = vk.API(self.session)
    

    api 属性添加到VKAuth self 对象。当你打电话时

    patch.object(accounts.auth_backends.vk_backend.VKAuth, 'api')
    

    您正在修补VKAuth 类的api 静态属性,而不是对象属性。

    您应该改为修补vk.API

    with patch('vk.API', autospec=True) as mock_api:
        response = self.client.post(reverse('auth-social', kwargs=dict(backend='vk')), dict(access_token=auth_token), follow=True)
    

    注意事项:

    1. 仅当您真正知道为什么需要它时才使用patch.object,而不是简单的patch
    2. autospec=True 不是强制性的,但 I strongly encourage to use it
    3. patch 上下文中self.api 将等于mock_api.return_value,因为调用vk.API(self.session) 就像调用mock_api();换句话说,mock_api 是用于替换 vk.API 引用的模拟对象。
    4. 看看where to patch,你会发现它很有用。

    现在,如果您想通过某种行为填充您的 mock_api.return_value,您可以在 with 上下文中进行配置:

    with patch('vk.API', autospec=True) as mock_api:
        m_api = mock_api.return_value
        m_api.friends.return_value = None
        m_api.friends.get.return_value = vk_ids
        .... Your test
    

    【讨论】:

    • 在我的例子中它返回:AttributeError: Mock object has no attribute 'friends'
    • 移除 autospec=true
    • 我看到的 vk.API 内部: class API(object): ... def getattr__(self, method_name): return Request(self, method_name) class Request(object): __slots = ('_api', '_method_name', '_method_args') def __init__(self, api, method_name): self._api = api self._method_name = method_name def __getattr__(self, method_name): return Request( self._api, self._method_name + '.' + method_name) def __call__(self, **method_args): self._method_args = method_args return self._api._session.make_request(self)
    • 当我从补丁中删除 autospec=True 时,它​​输出: ValueError: need more than 0 values to unpack Traceback (last last 最近调用): File "/home/pavel/api/accounts/tests/ test_views.py”,第 266 行,在 test_auth_vk response = self.client.post(reverse('auth-social', kwargs=dict(backend='vk')), dict(access_token=auth_token), follow=True)
    • 我现在帮不了你了。我稍后会回到它
    猜你喜欢
    • 1970-01-01
    • 2016-03-31
    • 1970-01-01
    • 1970-01-01
    • 2021-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-21
    相关资源
    最近更新 更多