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

pau*_*aus 1 python django unit-testing mocking python-mock

我正在尝试模拟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()
Run Code Online (Sandbox Code Playgroud)

来自测试模块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)
Run Code Online (Sandbox Code Playgroud)

在基于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)
Run Code Online (Sandbox Code Playgroud)

我得到一个错误:

AttributeError: <class 'accounts.auth_backends.vk_backend.VKAuth' doens't have the attribute 'api'
Run Code Online (Sandbox Code Playgroud)

我应该写什么而不是当前的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()
Run Code Online (Sandbox Code Playgroud)

但是在我们通过self.client.post()向django-rest-framework api请求之前,对它进行模拟。

谢谢!

Mic*_*ico 5

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

self.api = vk.API(self.session)
Run Code Online (Sandbox Code Playgroud)

apiVKAuth self 对象添加属性。你打电话的时候

patch.object(accounts.auth_backends.vk_backend.VKAuth, 'api')
Run Code Online (Sandbox Code Playgroud)

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

您应该改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)
Run Code Online (Sandbox Code Playgroud)

注意事项:

  1. patch.object仅当您真正知道为什么需要它时才使用它而不是简单patch
  2. autospec=True不是强制性的,但我强烈鼓励使用它
  3. patch上下文self.api中等于mock_api.return_value因为通话vk.API(self.session)就像通话mock_api(); 换句话说mock_api是用于替换vk.API引用的模拟对象。
  4. 看一下修补的位置,您会发现它非常有用。

现在,如果您想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
Run Code Online (Sandbox Code Playgroud)