【问题标题】:How to test response of service using mock?如何使用模拟测试服务响应?
【发布时间】:2021-09-24 09:34:01
【问题描述】:

我有以下服务和一个发布请求:

import requests

class Attt():
    def get_req(self):
        pload = {'username': 'Olivia', 'password': '123'}
        r = requests.post('https://httpbin.org/post', data=pload)
        print(r.text)
        print(r.content)


obj=Attt()
obj.get_req()

我想模拟请求,以便创建所需的服务响应。 我写了一个如下的测试:

import unittest
from unittest.mock import patch
from mock_tutorial.attt import Attt
import pandas as pd

class TestMockService(unittest.TestCase):

    def test_mock(self):

        fake_json = [{'test': "mock"}]

        with patch('mock_tutorial.attt.requests.post') as mock_get:
            mock_get.return_value.status_code = 200
            mock_get.return_value.json.return_value = fake_json

            obj = Attt()
            response = obj.get_req()
            print('response json', response.json())

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json(), fake_json)


if __name__ == "__main__":
    unittest.main()

但由于get_req 方法不返回响应,断言失败:AttributeError: 'NoneType' object has no attribute 'json'。如何修改断言以检查 get_req 方法的响应是否已被相应地模拟到 fake_json 变量?

【问题讨论】:

    标签: python json unit-testing service mocking


    【解决方案1】:

    这是因为你没有从Attt.get_req 返回任何东西,因此它返回None。所以在你的测试中,就像调用None.json()一样。您应该返回请求的响应:

    ...
    class Attt():
        def get_req(self):
            ...
            return r
    ...
    

    还如文档所述,您应该将代码包装在 attt.py 中,这样在导入时它就不会被执行:

    if __name__ == "__main__":
        obj=Attt()
        obj.get_req()
    

    这似乎很好,因为这只是用于教程。但请注意,您已经可以使用一些库来模拟 requests 模块,例如 requests-mock


    更新

    如果该方法不返回任何内容,那么我们所能做的就是向目标功能添加一个 spy 来检查。但请注意,这在现实中是没有意义的,因为我们正在监视的是一个模拟/修补的功能,它是 requests.post。这只会有利于学习目的,但实际上,最好不要添加测试,因为它根本不会增加任何价值。为此,我们可以使用pytest-mock

    mock_tutorial/attt.py

    import requests
    
    class Attt():
        def get_req(self):
            pload = {'username': 'Olivia', 'password': '123'}
            r = requests.post('https://httpbin.org/post', data=pload)
            print(r.text)
            print(r.content)
    

    test_attt.py

    from unittest.mock import patch
    from mock_tutorial import attt
    from mock_tutorial.attt import Attt
    
    import pytest
    
    
    def test_mock(mocker):  # Run <pip install pytest-mock>
        fake_json = [{'test': "mock"}]
    
        with patch('mock_tutorial.attt.requests.post') as mock_get:
            mock_get.return_value.status_code = 200
            mock_get.return_value.json.return_value = fake_json
    
            post_spy = mocker.spy(attt.requests, "post")
    
            obj = Attt()
            response = obj.get_req()
    
            assert post_spy.spy_return.status_code == 200
            assert post_spy.spy_return.json() == fake_json
    

    【讨论】:

    • 感谢您的回答。是否有一个选项可以在不返回响应值的情况下解决失败的断言?
    • 有那些断言?不,没有其他选择。因为断言检查方法self.assertEqual(response.status_code, 200) 的响应,其中response 来自方法response = obj.get_req() 的返回。如果你真的不打算返回任何东西并且仍然想测试调用,那么你应该像library那样实现一个间谍。
    • 除非你也愿意嘲笑Attt.get_req()?因为我们可以模拟它的返回值,这样它就不会返回None。但我认为这样的测试不再有意义,因为一切都会被嘲笑:)
    • 感谢您的解释。我想知道是否可以让get_req 不返回响应并找到一种方法来更改当前断言,以便他们可以检查响应是否已更改为fake_json
    • @IanKurtis 我更新了关于如何监视requests.post 并检查返回的答案。它现在回答你的问题了吗?
    猜你喜欢
    • 2021-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-21
    • 1970-01-01
    • 2017-03-14
    • 2017-03-08
    相关资源
    最近更新 更多