首页 > 解决方案 > 如何使用模拟测试服务响应?

问题描述

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

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 变量?

标签: pythonjsonunit-testingservicemocking

解决方案


这是因为您没有从中返回任何东西,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


更新

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

模拟教程/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

推荐阅读