Django单元测试和模拟请求模块

10

我是Mock的新手,正在编写一个针对此函数的单元测试:

# utils.py
import requests    

def some_function(user):
    payload = {'Email': user.email}
    url = 'http://api.example.com'
    response = requests.get(url, params=payload)      

    if response.status_code == 200:
       return response.json()
    else:
        return None

我正在使用Michael Foord的Mock库作为单元测试的一部分,并且在模拟response.json()以返回一个JSON结构时遇到了困难。这是我的单元测试:

# tests.py
from .utils import some_function

class UtilsTestCase(unittest.TestCase):
    def test_some_function(self):
        with patch('utils.requests') as mock_requests:
            mock_requests.get.return_value.status_code = 200
            mock_requests.get.return_value.content = '{"UserId":"123456"}'
            results = some_function(self.user)
            self.assertEqual(results['UserId'], '123456')

我在阅读文档后尝试了许多不同的模拟设置,但都没有成功。如果在我的单元测试中打印results,它总是显示以下内容,而不是我想要的JSON数据结构:

<MagicMock name=u'requests.get().json().__getitem__().__getitem__()' id='30315152'>

我在做什么事情上出了什么问题?

3个回答

17

使用json方法替代content方法。(在some_function中未使用content方法)

尝试以下代码。

import unittest

from mock import Mock, patch

import utils

class UtilsTestCase(unittest.TestCase):
    def test_some_function(self):
        user = self.user = Mock()
        user.email = 'user@example.com'
        with patch('utils.requests') as mock_requests:
            mock_requests.get.return_value = mock_response = Mock()
            mock_response.status_code = 200
            mock_response.json.return_value = {"UserId":"123456"}
            results = utils.some_function(self.user)
            self.assertEqual(results['UserId'], '123456')

非常感谢您的答复。现在它已经按照我所希望的方式运行了。 - Scott Woodall
我会给你的单元测试添加一个更多的断言测试,以确保使用预期参数调用了request.get方法。类似于 mock_get.assert_called_with('http://api.example.com', payload={'Email': self.user.email}。这将确保您的库代码使用预期参数进行请求调用,这与测试库代码的返回值(被Mocked)一样重要。 - adam
超棒的答案,谢谢! - java-addict301

2

我喜欢使用的另一种模式是在单元测试的setUp方法中启动补丁程序,这样更具可重用性。同时,检查模拟请求是否使用了预期的参数也非常重要:

class UtilsTestCase(TestCase):

    def setUp(self):
        self.user = Mock(id=123, email='foo@bar.com')

        patcher = patch('utils.requests.get')
        self.mock_response = Mock(status_code=200)
        self.mock_response.raise_for_status.return_value = None
        self.mock_response.json.return_value = {'UserId': self.user.id}
        self.mock_request = patcher.start()
        self.mock_request.return_value = self.mock_response

    def tearDown(self):
        self.mock_request.stop()

    def test_request(self):
        results = utils.some_function(self.user)

        self.assertEqual(results['UserId'], 123)

        self.mock_request.assert_called_once_with(
            'http://api.example.com'
            payload={'Email': self.user.email},
        )

    def test_bad_request(self):
        # override defaults and reassign
        self.mock_response.status_code = 500
        self.mock_request.return_value = self.mock_response
        results = utils.some_function(self.user)

        self.assertEqual(results, None)

        self.mock_request.assert_called_once_with(
            'http://api.example.com'
            payload={'Email': user.email},
        )

0

我认为另一种更清晰直接的方式是:

import unittest

from mock import Mock, patch

import utils

class UtilsTestCase(unittest.TestCase):
    def test_some_function(self):
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"UserId": "123456"}
        with patch('utils.requests.get') as mock_requests:
            results = utils.some_function(self.user)
            self.assertEqual(results['UserId'], '123456')

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