【问题标题】:Mocking Django Tests - mocked function still executed模拟 Django 测试 - 模拟函数仍在执行
【发布时间】:2021-03-02 19:18:36
【问题描述】:

我正在编写一个与第三方 API 集成的基本 Django 应用程序。以下是所涉及文件的基本文件夹结构:

timecardsite/
    __init__.py
    tests/
        __init__.py
        tests.py
    services.py
    views.py
    ...

services.py 文件包含与第三方 API 直接交互的各种函数。它们通常会返回包含我需要在视图中使用的数据的字典。

在我的测试文件中,特别是在我测试视图的类中,我试图模拟其中一些服务函数的返回值,所以理想情况下它们甚至不会被调用(我测试服务函数直接在另一个类中):

tests.py

from unittest.mock import patch
from django.test import TestCase

import timecardsite.services as services
from timecardsite.models import Account

...

class ViewsTests(TestCase):

    @patch('timecardsite.views.services.get_access_token')
    @patch('timecardsite.views.services.get_account_info')
    def test_auth_view_saves_tokens_and_account_info_to_db(self, mock_access, mock_account):

        # Mock both get_access_token and get_account_info
        code = generate_random_token()
        access_token = generate_random_token()
        refresh_token = generate_random_token()
        account_id = generate_random_token(length=5)
        name = 'Example name'

        mock_access.return_value = {
            'access_token': access_token,
            'refresh_token': refresh_token
        }

        mock_account.return_value = {
            'account_id': account_id,
            'name': name
        }

        self.client.get(f'/auth/?code={code}')

        self.assertEqual(Account.objects.count(), 1)
        new_account = Account.objects.first()
        self.assertEqual(new_account.account_id, account_id)
        self.assertEqual(new_account.name, name)
        self.assertEqual(new_account.access_token, access_token)
        self.assertEqual(new_account.refresh_token, refresh_token)

调用这些函数的视图:

views.py

from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse

import timecardsite.services as services
from timecardsite.models import Account

def auth(request):
    # capture code from url
    code = request.GET.get('code')

    if code:
        # request tokens
        tokens = services.get_access_token(code)

        # request account info
        account_info = services.get_account_info(tokens['access_token'])

        # create Account object
        account = Account(
            account_id = account_info['account_id'],
            access_token = tokens['access_token'],
            refresh_token = tokens['refresh_token'],
            name = account_info['name']
        )
        account.save()

        return HttpResponseRedirect(reverse('dashboard'))

这是我得到的错误 - 很明显,模拟的服务函数正在被调用,并且失败了,因为它试图用我生成的假代码来访问实际的 API。

..........E
======================================================================
ERROR: test_auth_view_saves_tokens_and_account_info_to_db (timecardsite.tests.tests.ViewsTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/holden/.pyenv/versions/3.7.10/lib/python3.7/unittest/mock.py", line 1256, in patched
    return func(*args, **keywargs)
  File "/Users/holden/Projects/timesheet/timecardsite/tests/tests.py", line 193, in test_auth_view_saves_tokens_and_account_info_to_db
    self.client.get(f'/auth/?code={code}')
  File "/Users/holden/.pyenv/versions/timesheet/lib/python3.7/site-packages/django/test/client.py", line 739, in get
    response = super().get(path, data=data, secure=secure, **extra)
  File "/Users/holden/.pyenv/versions/timesheet/lib/python3.7/site-packages/django/test/client.py", line 395, in get
    **extra,
  File "/Users/holden/.pyenv/versions/timesheet/lib/python3.7/site-packages/django/test/client.py", line 470, in generic
    return self.request(**r)
  File "/Users/holden/.pyenv/versions/timesheet/lib/python3.7/site-packages/django/test/client.py", line 716, in request
    self.check_exception(response)
  File "/Users/holden/.pyenv/versions/timesheet/lib/python3.7/site-packages/django/test/client.py", line 577, in check_exception
    raise exc_value
  File "/Users/holden/.pyenv/versions/timesheet/lib/python3.7/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/Users/holden/.pyenv/versions/timesheet/lib/python3.7/site-packages/django/core/handlers/base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/holden/Projects/timesheet/timecardsite/views.py", line 19, in auth
    account_info = services.get_account_info(tokens['access_token'])
KeyError: 'access_token'

----------------------------------------------------------------------
Ran 11 tests in 0.074s

如何成功模拟这些函数?

谢谢。

【问题讨论】:

    标签: python django unit-testing mocking


    【解决方案1】:

    想通了。一旦我修补了整个services 模块,它就成功地挂上了;但是,我开始收到另一个错误,因为视图中的 Account.save() 试图将 MagicMock 函数保存到数据库中。总而言之,现在的测试是这样的:

        def test_auth_view_saves_tokens_and_account_info_to_db(self):
            # Mock both get_access_token and get_account_info
            tokens = {
                'access_token': generate_random_token(),
                'refresh_token': generate_random_token()
            }
    
            account_info = {
                'account_id': generate_random_token(length=5),
                'name': 'Example Name'
            }
            code = generate_random_token()
    
    
            with patch('timecardsite.views.services') as mocked_services:
                mocked_services.get_access_token.return_value = tokens
                mocked_services.get_access_token.__getitem__.side_effect = tokens.__getitem__
                mocked_services.get_account_info.return_value = account_info
                mocked_services.get_account_info.__getitem__.side_effect = account_info.__getitem__
                self.client.get(f'/auth/?code={code}')
    
            self.assertEqual(Account.objects.count(), 1)
            new_account = Account.objects.first()
            self.assertEqual(new_account.account_id, account_info['account_id'])
            self.assertEqual(new_account.name, account_info['name'])
            self.assertEqual(new_account.access_token, tokens['access_token'])
            self.assertEqual(new_account.refresh_token, tokens['refresh_token'])
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-22
      • 1970-01-01
      • 1970-01-01
      • 2021-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多