【问题标题】:How correctly mock class dependencies?如何正确模拟类依赖项?
【发布时间】:2021-12-13 12:25:48
【问题描述】:

我最近开始学习如何使用来自 unittest 库的模拟,但在弄清楚如何正确模拟类依赖项时遇到了问题。 下面是我试图模拟的例子

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 对象注入到 Post 类中

据我了解,我需要模拟 self.client.request,这可以用请求获取或其他方式替换?

数据.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) 此处调用的原始响应?

也许有人可以解释一下这是如何工作的

谢谢!

【问题讨论】:

    标签: python unit-testing python-unittest python-mock


    【解决方案1】:

    由于使用依赖注入,所以不需要使用mock.patch()。只需创建模拟的 HttpClient 对象并将其传递给 Post 类。如果你的模块依赖于import关键字导入的一些模块,那么你需要使用mock.patch()的东西来模拟它们。

    此外,您可以为client.request() 方法创建模拟的Response。我们可以使用 requests 包中的 Response 类。

    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/stackoverflow/69751753/client.py          7      4    43%   6-9
    src/stackoverflow/69751753/data.py           10      1    90%   16
    src/stackoverflow/69751753/test_data.py      18      0   100%
    -----------------------------------------------------------------------
    TOTAL                                        35      5    86%
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-10
      • 1970-01-01
      相关资源
      最近更新 更多