unit testing使用请求库的Python应用程序
我正在编写一个使用Kenneth Reitz的请求库来执行REST操作的应用程序,我正在努力寻找一种很好的方式来对这些应用程序进行unit testing,因为请求通过模块级别的方法提供了它的方法。
我想要的是综合双方对话的能力; 提供一系列请求断言和响应。
如果您使用特定请求尝试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"
HTTPrettyfunction更丰富 – 它还提供模拟状态码,stream式响应,旋转响应,dynamic响应(带callback)。
您可以使用模拟库(如Mocker)拦截对请求库的调用并返回指定的结果。
作为一个非常简单的例子,考虑使用请求库的这个类:
class MyReq(object): def doSomething(self): r = requests.get('https://api.github.com', auth=('user', 'pass')) return r.headers['content-type']
这里是一个unit testing,拦截对requests.get
的调用并返回一个指定的testing结果:
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()
当我运行这个unit testing时,我得到以下结果:
. ---------------------------------------------------------------------- Ran 1 test in 0.004s OK
实际上有一点奇怪,图书馆有一个关于最终用户unit testing的空白页面,同时针对用户友好性和易用性。 不过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)
使用嘲笑像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_stringreplace响应中的.json()。
这些答案中缺less的是请求 – 模拟 。
从他们的页面:
>>> import requests >>> import requests_mock
作为上下文pipe理器:
>>> 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'