【问题标题】:Unit testing a python app that uses the requests library对使用 requests 库的 python 应用程序进行单元测试
【发布时间】:2012-03-22 12:43:39
【问题描述】:

我正在编写一个使用 Kenneth Reitz 的 requests library 执行 REST 操作的应用程序,我正在努力寻找一种对这些应用程序进行单元测试的好方法,因为 requests 通过模块级方法提供其方法。

我想要的是合成双方对话的能力;提供一系列请求断言和响应。

【问题讨论】:

  • 所以你需要模拟出 REST 服务器?
  • 为什么这会使 unittest 不合适?检查库如何进行自己的单元测试;可能会提供想法。
  • FWIW,Requests 库使用实时 URL(github.com、作者自己的域等)进行自己的测试。

标签: python unit-testing testing mocking python-requests


【解决方案1】:

实际上有点奇怪,该库有一个关于最终用户单元测试的空白页面,同时以用户友好性和易用性为目标。然而,Dropbox 有一个易于使用的库,不出所料地称为responses。这是它的intro post。它说他们没有使用httpretty,但没有说明失败的原因,并编写了一个具有类似 API 的库。

import unittest

import requests
import responses


class TestCase(unittest.TestCase):

  @responses.activate  
  def testExample(self):
    responses.add(**{
      'method'         : responses.GET,
      'url'            : 'http://example.com/api/123',
      'body'           : '{"error": "reason"}',
      'status'         : 404,
      'content_type'   : 'application/json',
      'adding_headers' : {'X-Foo': 'Bar'}
    })

    response = requests.get('http://example.com/api/123')

    self.assertEqual({'error': 'reason'}, response.json())
    self.assertEqual(404, response.status_code)

【讨论】:

  • 更新了 responses intro post 的 URL
  • 有趣的是,由于这是作为答案发布的,因此该库的作者 David Cramer 继续前进并创建了 Sentry 和 moved the library with him。这就是为什么在 GitHub 上它位于 getsentry org 下的原因。
【解决方案2】:

这些答案中缺少requests-mock

来自他们的页面:

>>> import requests
>>> import requests_mock

作为上下文管理器:

>>> with requests_mock.mock() as m:

...     m.get('http://test.com', text='data')
...     requests.get('http://test.com').text
...
'data'

或者作为装饰者:

>>> @requests_mock.mock()
... def test_func(m):
...     m.get('http://test.com', text='data')
...     return requests.get('http://test.com').text
...
>>> test_func()
'data'

【讨论】:

  • 您知道如何使用pytest 进行这项工作吗?我尝试了您引用的确切示例。参考:stackoverflow.com/questions/47703748/…
  • 如果我没记错的话,我使用了装饰器。我认为这也适用于 pytest。
  • 我让它与装饰器一起工作,但它似乎(在我的系统上)与其他一些参数冲突,所以我必须将 kw 参数传递给 Mocker,如 docs 中所述.不确定这是否与 pytest 有关,但出现的错误提到了固定装置。感谢您回到这个问题。
【解决方案3】:

如果您使用特定的请求,请尝试httmock。它非常简单和优雅:

from httmock import urlmatch, HTTMock
import requests

# define matcher:
@urlmatch(netloc=r'(.*\.)?google\.com$')
def google_mock(url, request):
    return 'Feeling lucky, punk?'

# open context to patch
with HTTMock(google_mock):
    # call requests
    r = requests.get('http://google.com/')
print r.content  # 'Feeling lucky, punk?'

如果您想要更通用的东西(例如模拟任何进行 http 调用的库),请选择 httpretty

几乎一样优雅:

import requests
import httpretty

@httpretty.activate
def test_one():
    # define your patch:
    httpretty.register_uri(httpretty.GET, "http://yipit.com/",
                        body="Find the best daily deals")
    # use!
    response = requests.get('http://yipit.com')
    assert response.text == "Find the best daily deals"

HTTPretty 的功能要丰富得多 - 它还提供模拟状态代码、流式响应、旋转响应、动态响应(带有回调)。

【讨论】:

  • httpretty 的东西是天外来客,谢谢!
【解决方案4】:

在 srgerg 的回答中使用嘲弄器:

def replacer(method, endpoint, json_string):
    from mocker import Mocker, ANY, CONTAINS
    mocker = Mocker()
    result = mocker.mock()
    result.json()
    mocker.count(1, None)
    mocker.result(json_string)
    replacement = mocker.replace("requests." + method)
    replacement(CONTAINS(endpoint), params=ANY)
    self.mocker.result(result)
    self.mocker.replay()

对于 requests 库,这将通过您正在点击的方法和端点拦截请求,并将响应中的 .json() 替换为传入的 json_string。

【讨论】:

    【解决方案5】:

    您可以使用诸如Mocker 之类的模拟库来拦截对请求库的调用并返回指定的结果。

    作为一个非常简单的例子,考虑这个使用请求库的类:

    class MyReq(object):
        def doSomething(self):
            r = requests.get('https://api.github.com', auth=('user', 'pass'))
            return r.headers['content-type']
    

    下面是一个单元测试,拦截对requests.get的调用,返回指定的结果进行测试:

    import unittest
    import requests
    import myreq
    
    from mocker import Mocker, MockerTestCase
    
    class MyReqTests(MockerTestCase):
        def testSomething(self):
            # Create a mock result for the requests.get call
            result = self.mocker.mock()
            result.headers
            self.mocker.result({'content-type': 'mytest/pass'})
    
            # Use mocker to intercept the call to requests.get
            myget = self.mocker.replace("requests.get")
            myget('https://api.github.com', auth=('user', 'pass'))
            self.mocker.result(result)
    
            self.mocker.replay()
    
            # Now execute my code
            r = myreq.MyReq()
            v = r.doSomething()
    
            # and verify the results
            self.assertEqual(v, 'mytest/pass')
            self.mocker.verify()
    
    if __name__ == '__main__':
        unittest.main()
    

    当我运行这个单元测试时,我得到以下结果:

    .
    ----------------------------------------------------------------------
    Ran 1 test in 0.004s
    
    OK
    

    【讨论】:

      猜你喜欢
      • 2012-03-21
      • 2011-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-01
      • 2016-04-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多