繁体   English   中英

模拟python3时单元测试失败

[英]Unit test failing when mocking python3

我正在尝试模拟 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'

我在这里做错了什么?

问题是您正在配置类( 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提供更方便的接口(如果这就是您在这里尝试做的),那么您可能会将测试视为特征测试,而根本不使用测试替身。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM