Как издеваться над атрибутом класса, созданным в __init__?

Я пытаюсь издеваться над методом self.api.friends.get в классе VKAuth:

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)

создав экземпляр класса VKAuth во время пост-вызова auth-social выше в представлении на основе классов SNView:

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

но подделайте это за тот момент, когда мы сделаем запрос к api django-rest-framework через self.client.post ().

Спасибо!


person Павел Иванов    schedule 21.01.2016    source источник


Ответы (1)


Вы не то исправляете. В 1_:

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

добавить атрибут api к VKAuth self объекту. Когда ты звонишь

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

вы исправляете статический атрибут 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)

Примечания :

  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
person Michele d'Amico    schedule 21.01.2016
comment
В моем случае он возвращает: AttributeError: объект Mock не имеет атрибута "друзья" - person Павел Иванов; 21.01.2016
comment
Удалить autospec = true - person Michele d'Amico; 21.01.2016
comment
Внутри 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 + '.' + имя_метода) def __call __ (self, ** method_args): self._method_args = method_args return self._api._session.make_request (self) - person Павел Иванов; 21.01.2016
comment
Когда я удалил autospec = True из патча, он выводит: ValueError: требуется более 0 значений для распаковки Traceback (последний вызов последний): 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) - person Павел Иванов; 21.01.2016
comment
Я не могу вам помочь сейчас. Я вернусь к этому позже - person Michele d'Amico; 21.01.2016
comment
Вроде как возможная причина: 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, что мы используем для аутентификация. - person Павел Иванов; 21.01.2016
comment
Как уберечь пользователей self.api. от издевательства? - person Павел Иванов; 21.01.2016
comment
Извини, я сейчас занят. Я не могу следить за тобой сейчас. - person Michele d'Amico; 21.01.2016