79

Kenneth Reitzのリクエストライブラリを使用してREST操作を実行するアプリケーションを作成していますが、リクエストはモジュールレベルのメソッドを介してメソッドを提供するため、これらのアプリケーションを単体テストするための優れた方法を見つけるのに苦労しています。

私が欲しいのは、両者の間の会話を統合する能力です。一連の要求アサーションと応答を提供します。

4

6 に答える 6

58

実際、ライブラリにエンドユーザーの単体テストに関する空白のページがあり、使いやすさと使いやすさを目標としているのは少し奇妙です。ただし、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)
于 2014-11-22T13:02:28.873 に答える
50

特にリクエストを使用する場合は、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ははるかに機能が豊富で、モックステータスコード、ストリーミングレスポンス、ローテーションレスポンス、ダイナミックレスポンス(コールバック付き)も提供します。

于 2013-08-28T14:19:32.443 に答える
22

Mockerなどのモックライブラリを使用して、リクエストライブラリへの呼び出しをインターセプトし、指定された結果を返すことができます。

非常に簡単な例として、requestsライブラリを使用するこのクラスについて考えてみます。

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-05T00:48:19.427 に答える
3

これらの答えに欠けているのは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'
于 2017-06-24T10:48:56.867 に答える
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()が渡されたjson_stringに置き換えられます。

于 2013-05-12T02:46:59.583 に答える
0

応答ハンドラー/パーサーを別の関数に分割するとrequests.Response objects、クライアントとサーバーの相互作用を模倣することなく、直接操作できます。

テスト中のコード

from xml.dom import minidom
from requests.models import Response

def function_under_test(s3_response: Response):
    doc = minidom.parseString(s3_response.text)

    return (
        s3_response.status_code,
        doc.getElementsByTagName('Code').item(0).firstChild.data,
    )

テストコード

import unittest
from io import BytesIO

class Test(unittest.TestCase):

    def test_it(self):
        s3_response = Response()
        s3_response.status_code = 404
        s3_response.raw = BytesIO(b"""<?xml version="1.0" encoding="UTF-8"?>
            <Error>
            <Code>NoSuchKey</Code>
            <Message>The resource you requested does not exist</Message>
            <Resource>/mybucket/myfoto.jpg</Resource> 
            <RequestId>4442587FB7D0A2F9</RequestId>
            </Error>
        """)

        parsed_response = function_under_test(s3_response)

        self.assertEqual(404, parsed_response[0])
        self.assertEqual("NoSuchKey", parsed_response[1])
于 2022-03-01T17:54:03.863 に答える