如何正确模拟 class 依赖项?
How correctly mock class dependencies?
我最近开始学习如何使用 unittest
库中的 mock,但在弄清楚如何正确模拟 class 依赖项时遇到了问题。
下面是我试图模拟的例子
client.py
class HttpClient:
def request(self, method, url, params = None):
if method == "GET":
return requests.get(url)
elif method == "POST":
return requests.post(url, body=params)
这里我将HttpClient对象注入到Postclass
据我所知,我需要模拟 self.client.request
,这可以用 requests get 或其他方式替换吗?
data.py
class Post:
def __init__(self, client: HttpClient):
self.client = client
self.base_url = "https://jsonplaceholder.typicode.com"
def get_posts(self, amount):
response = self.client.request(method="GET", url=f"{self.base_url}/posts/{amount}")
if response.ok:
return response.json()
return response.status_code
现在是测试部分
test_data.py
class TestPost(unittest.TestCase):
@patch('app.client.HttpClient')
def setUp(self, module):
self.mock_http = MagicMock(autospec=HttpClient)
self.mock_post = Post(self.mock_http)
@patch.object(requests, 'get')
def test_get_posts(self, mock_data):
mock_data.return_value = {
'postId': 1,
'title': 'My title',
'description': 'Post description'
}
response = self.mock_post.get_posts(1)
assert response['postId'] == 1
当我设置 mock_data.return_value
时,它实际上是否替换了此处调用的原始响应 response = self.mock_post.get_posts(1)
或不是?
也许有人可以解释这是如何工作的
谢谢!
因为你使用了依赖注入,所以你不需要使用mock.patch()
。只需创建模拟的 HttpClient
对象并将其传递给 Post
class。如果你的模块依赖于一些通过 import
关键字导入的模块,那么你需要使用 mock.patch()
东西来模拟它们。
此外,您可以为 client.request()
方法创建模拟 Response
。我们可以使用 requests
包中的 Response
class。
data.py
:
from client import HttpClient
class Post:
def __init__(self, client: HttpClient):
self.client = client
self.base_url = "https://jsonplaceholder.typicode.com"
def get_posts(self, amount):
response = self.client.request(method="GET", url=f"{self.base_url}/posts/{amount}")
if response.ok:
return response.json()
return response.status_code
test_data.py
:
import unittest
from requests import Response
from unittest.mock import MagicMock, Mock
from client import HttpClient
from data import Post
class TestPost(unittest.TestCase):
def setUp(self):
self.mock_http = MagicMock(autospec=HttpClient)
self.mock_post = Post(self.mock_http)
def test_get_posts(self):
mock_response = Mock(spec=Response)
mock_response.json.return_value = {
'postId': 1,
'title': 'My title',
'description': 'Post description'
}
self.mock_http.request.return_value = mock_response
response = self.mock_post.get_posts(1)
self.mock_http.request.assert_called_once_with(method="GET", url="https://jsonplaceholder.typicode.com/posts/1")
assert response['postId'] == 1
if __name__ == '__main__':
unittest.main(verbosity=2)
测试结果:
test_get_posts (__main__.TestPost) ... ok
----------------------------------------------------------------------
Ran 1 test in 0.002s
OK
Name Stmts Miss Cover Missing
-----------------------------------------------------------------------
src/Whosebug/69751753/client.py 7 4 43% 6-9
src/Whosebug/69751753/data.py 10 1 90% 16
src/Whosebug/69751753/test_data.py 18 0 100%
-----------------------------------------------------------------------
TOTAL 35 5 86%
我最近开始学习如何使用 unittest
库中的 mock,但在弄清楚如何正确模拟 class 依赖项时遇到了问题。
下面是我试图模拟的例子
client.py
class HttpClient:
def request(self, method, url, params = None):
if method == "GET":
return requests.get(url)
elif method == "POST":
return requests.post(url, body=params)
这里我将HttpClient对象注入到Postclass
据我所知,我需要模拟 self.client.request
,这可以用 requests get 或其他方式替换吗?
data.py
class Post:
def __init__(self, client: HttpClient):
self.client = client
self.base_url = "https://jsonplaceholder.typicode.com"
def get_posts(self, amount):
response = self.client.request(method="GET", url=f"{self.base_url}/posts/{amount}")
if response.ok:
return response.json()
return response.status_code
现在是测试部分
test_data.py
class TestPost(unittest.TestCase):
@patch('app.client.HttpClient')
def setUp(self, module):
self.mock_http = MagicMock(autospec=HttpClient)
self.mock_post = Post(self.mock_http)
@patch.object(requests, 'get')
def test_get_posts(self, mock_data):
mock_data.return_value = {
'postId': 1,
'title': 'My title',
'description': 'Post description'
}
response = self.mock_post.get_posts(1)
assert response['postId'] == 1
当我设置 mock_data.return_value
时,它实际上是否替换了此处调用的原始响应 response = self.mock_post.get_posts(1)
或不是?
也许有人可以解释这是如何工作的
谢谢!
因为你使用了依赖注入,所以你不需要使用mock.patch()
。只需创建模拟的 HttpClient
对象并将其传递给 Post
class。如果你的模块依赖于一些通过 import
关键字导入的模块,那么你需要使用 mock.patch()
东西来模拟它们。
此外,您可以为 client.request()
方法创建模拟 Response
。我们可以使用 requests
包中的 Response
class。
data.py
:
from client import HttpClient
class Post:
def __init__(self, client: HttpClient):
self.client = client
self.base_url = "https://jsonplaceholder.typicode.com"
def get_posts(self, amount):
response = self.client.request(method="GET", url=f"{self.base_url}/posts/{amount}")
if response.ok:
return response.json()
return response.status_code
test_data.py
:
import unittest
from requests import Response
from unittest.mock import MagicMock, Mock
from client import HttpClient
from data import Post
class TestPost(unittest.TestCase):
def setUp(self):
self.mock_http = MagicMock(autospec=HttpClient)
self.mock_post = Post(self.mock_http)
def test_get_posts(self):
mock_response = Mock(spec=Response)
mock_response.json.return_value = {
'postId': 1,
'title': 'My title',
'description': 'Post description'
}
self.mock_http.request.return_value = mock_response
response = self.mock_post.get_posts(1)
self.mock_http.request.assert_called_once_with(method="GET", url="https://jsonplaceholder.typicode.com/posts/1")
assert response['postId'] == 1
if __name__ == '__main__':
unittest.main(verbosity=2)
测试结果:
test_get_posts (__main__.TestPost) ... ok
----------------------------------------------------------------------
Ran 1 test in 0.002s
OK
Name Stmts Miss Cover Missing
-----------------------------------------------------------------------
src/Whosebug/69751753/client.py 7 4 43% 6-9
src/Whosebug/69751753/data.py 10 1 90% 16
src/Whosebug/69751753/test_data.py 18 0 100%
-----------------------------------------------------------------------
TOTAL 35 5 86%