如何模拟在__init__中实例化的类属性?

3

我正在尝试模拟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”进行POST调用创建了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并进行模拟?

更新:

更准确地说,我想要一个等价于:

    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发送请求之前,要进行模拟测试。

谢谢!

1个回答

6
你正在修补错误的事情。在 VKAuth 中:
self.api = vk.API(self.session)

需要在VKAuthself对象中添加api属性。当你调用时。

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 时,才使用 patch.object
  2. autospec=True 不是必需的,但我强烈建议使用它
  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对象没有'friends'属性。 - Павел Иванов
去掉 autospec=true - Michele d'Amico
在 vk.API 中,我看到:类 API(object): ... def getattr(self, method_name): return Request(self, method_name)类 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: 需要多于0个值来解包Traceback(最近的调用): File "/home/pavel/api/accounts/tests/test_views.py", line 266, in test_auth_vk response = self.client.post(reverse('auth-social', kwargs=dict(backend='vk')), dict(access_token=auth_token), follow=True) - Павел Иванов
看起来可能的原因是:class VKAuth(object): def init(self, access_token, user): self.session = vk.Session(access_token=access_token) self.api = vk.API(self.session) ...def authenticate(self): try: vk_uid = self.api.users.get()[0]['uid']换句话说,我们已经模拟了vk.API中的所有内容,包括我们用于身份验证的api.users.get。 - Павел Иванов
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接