python - 模拟python3时单元测试失败
问题描述
我正在尝试模拟 python3requests
这是我的单元测试:
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
replaces 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 一样。解决这种紧张的一种方法是创建Session
外部MyWebSession
并将实例作为参数传递。然后测试变得更加直接,你可以摆脱你并不真正感兴趣的补丁和测试替身。
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
与测试无关(也许您将其放在那里进行调试?)。这无济于事,但我将其保留在您的代码中,以尽可能少地偏离它。
推荐阅读
- javascript - 如何使用 cypress test 登录微软帐户
- php - 如何验证yii2中的特定字段
- css - 将 CSS 放入文档中的最佳和最快的方法是什么?
- android - 停止 BottomNavigationView 重新创建片段
- prototype - 滚动时,如何使元素仅固定在 Figma 的特定部分中?
- visual-studio-code - 将变量 TM_FILENAME_BASE 分成两部分用于 VScode 的片段
- r - 如何根据日期组的最新日期过滤数据?
- postgresql - 从表 A 中获取所有记录,而不是从表 B 中为空
- npm - 在 NPM 模块中导出 Sass
- python-3.x - 解析 SEC 表格数据