【问题标题】:Unit test failing when mocking python3模拟python3时单元测试失败
【发布时间】:2019-08-05 12:34:57
【问题描述】:

我正在尝试模拟 python3 requests

这是我的单元测试:

from source.automation.my_web_session import MyWebSession
from unittest.mock import patch

@patch('requests.Session', autospec=True)
def test_initialize_session(mock_session):
    # Arrange
    user_agent = 'mobile user agent'
    mock_session.headers = {'user-agent' : user_agent}
    csrftoken = 'csrftoken'
    mock_session.cookies = {'csrftoken' : csrftoken}
    my_web_session = MyWebSession()
    # Act
    print(my_web_session.session.cookies)
    # Assert
    assert my_web_session.session.cookies['csrftoken'] == csrftoken

这是我正在测试的课程:

import requests

class MyWebSession:

    def __init__(self):
        self.session = requests.Session()

我正在尝试模拟用户代理的设置和更新。但是,运行我的测试时出现以下错误:

================================== FAILURES ===================================
___________________________ test_initialize_session ___________________________

mock_session = <MagicMock name='Session' spec='Session' id='81084944'>

    @patch('requests.Session', autospec=True)
    def test_initialize_session(mock_session):
        # Arrange
        user_agent = 'mobile user agent'
        mock_session.headers = {'user-agent' : user_agent}
        csrftoken = 'csrftoken'
        mock_session.cookies = {'csrftoken' : csrftoken}
        my_web_session = MyWebSession()
        # Act
>       print(my_web_session.session.cookies)

my_web_session_test.py:14:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <NonCallableMagicMock name='Session()' spec='Session' id='80148752'>
name = 'cookies'

    def __getattr__(self, name):
        if name in {'_mock_methods', '_mock_unsafe'}:
            raise AttributeError(name)
        elif self._mock_methods is not None:
            if name not in self._mock_methods or name in _all_magics:
>               raise AttributeError("Mock object has no attribute %r" % name)
E               AttributeError: Mock object has no attribute 'cookies'

我在这里做错了什么?

【问题讨论】:

  • 哪里出错了?
  • 我已经用完整的回溯编辑了 OP。
  • 添加mocked_session.headers = {}
  • 执行mocked_session.headers = {'user-agent':'some_user_agent'} 有效...但是为什么呢?
  • 因为现在mocked_session 确实有一个属性headers

标签: python unit-testing mocking python-requests


【解决方案1】:

问题是您正在配置类(mock_session 替换 Session)但您想要配置实例,这是它的返回值。你可以这样做,但它不会是一个很好的测试。这对我有用:

@patch('requests.Session', autospec=True)
def test_initialize_session(mock_session_cls):
    # Arrange
    mock_session = Mock(spec=[])
    user_agent = 'mobile user agent'
    mock_session.headers = {'user-agent' : user_agent}
    csrftoken = 'csrftoken'
    mock_session.cookies = {'csrftoken' : csrftoken}
    mock_session_cls.return_value = mock_session
    my_web_session = MyWebSession()
    # Act
    print(my_web_session.session.cookies)
    # Assert
    assert my_web_session.session.cookies['csrftoken'] == csrftoken

有些人可能更喜欢下面的版本(我也不喜欢):

def test_initialize_session():
    # Arrange
    mock_session = Mock(spec=[])
    user_agent = 'mobile user agent'
    mock_session.headers = {'user-agent' : user_agent}
    csrftoken = 'csrftoken'
    mock_session.cookies = {'csrftoken' : csrftoken}
    with patch('requests.Session', autospec=True, return_value=mock_session):
        my_web_session = MyWebSession()
    # Act
    print(my_web_session.session.cookies)
    # Assert
    assert my_web_session.session.cookies['csrftoken'] == csrftoken

我觉得这个测试的丑陋之处在于,当您只对第二个测试替身感兴趣时,您将一个测试替身存根以返回另一个测试替身。对我来说,这似乎是一个太多的测试替身。

根本问题是 Session 实例的创建作为实现细节隐藏在 __init__ 方法中,但您的测试希望控制它,就好像它是公共 API。解决这种紧张的一种方法是在MyWebSession 之外创建Session 并将实例作为参数传递。然后测试变得更加简单,您可以摆脱您并不真正感兴趣的补丁和测试替身。

class MyWebSession:
    def __init__(self, session):
        self.session = session

还有测试:

def test_initialize_session():
    # Arrange
    user_agent = 'mobile user agent'
    mock_session = Mock(spec=[])
    mock_session.headers = {'user-agent' : user_agent}
    csrftoken = 'csrftoken'
    mock_session.cookies = {'csrftoken' : csrftoken}
    my_web_session = MyWebSession(mock_session)
    # Act
    print(my_web_session.session.cookies)
    # Assert
    assert my_web_session.session.cookies['csrftoken'] == csrftoken

在生产代码中你会使用类似的东西

my_web_session = MyWebSession(requests.Session())

另一种方法是将MyWebSession 视为一个瘦包装器,为requests.Session 提供更方便的接口(如果您在此处尝试这样做),那么您可能会将测试视为characterization tests 而不是完全使用测试替身。

请注意,# Act 注释放错了位置,对print 的调用与测试无关(也许你把它放在那里是为了调试?)。这无济于事,但我将其保留在您的代码中,以尽可能少地偏离它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-16
    • 2019-10-10
    • 2020-01-08
    • 1970-01-01
    • 2021-01-23
    • 2020-03-24
    • 1970-01-01
    相关资源
    最近更新 更多