对使用请求库的python应用程序进行单元测试


70

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

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


2
因此,您需要模拟REST服务器吗?
kgr 2012年

为什么这会使单元测试不合适?检查库如何进行自己的单元测试;可能会提供一些想法。
John Mee 2012年

1
FWIW,请求库使用实时URL(github.com,作者自己的域,等等)进行自己的测试。
罗姆洛克

Answers:


46

实际上,该库在针对用户友好性和易用性的同时,有一个空白页面,涉及最终用户单元测试,这有点奇怪。但是,Dropbox有一个易于使用的库,毫不奇怪地称为responses。这是它的介绍性帖子。它说他们没有使用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)


有趣的是,自从将其作为答案发布以来,作者戴维·克莱默(David Cramer)(该图书馆的作者)继续前进并创立了Sentry,并随他一起搬迁了图书馆。这就是为什么在GitHub上它在getsentry组织下的原因。
bfontaine

49

如果您专门使用请求,请尝试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的东西是从这个世界直接出来的,谢谢!
mccc 2015年

22

您可以使用模拟库(例如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

2

在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()

对于请求库,这将按您所单击的方法和端点拦截请求,并使用传入的json_string替换响应上的.json()。


2

这些答案中缺少的是request-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/...
托马斯Fauskanger

如果我没记错的话,我使用了装饰器。而且我认为这也适用于pytest。
Unapiedra

我使它与装饰器一起使用,但是(在我的系统上)它似乎与其他参数冲突,因此我不得不将KW参数传递给Mocker,如docs中所述。不知道这是否与pytest有关,但是出现的错误提到了固定装置。感谢您回到问题所在。
Thomas Fauskanger
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.