web-dev-qa-db-ja.com

要求ライブラリを使用するpythonアプリの単体テスト

私はREST Kenneth Reitzの requestsライブラリ を使用して操作を実行するアプリケーションを作成しています。モジュールレベルのメソッドを介したメソッド。

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

59
Chris R

特にリクエストを使用する場合は、 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ははるかに機能が豊富です-また、モックステータスコード、ストリーミング応答、回転応答、動的応答(コールバック付き)も提供します。

42
Marek Brzóska

実際、ユーザーフレンドリーと使いやすさをターゲットにしながら、ライブラリにエンドユーザーのユニットテストに関する空白ページがあるのは少し奇妙です。ただし、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)
31
saaj

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 __== '__main__':
    unittest.main()

この単体テストを実行すると、次の結果が得られます。

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

OK
23
srgerg

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に置き換えます。

2
jchysk

これらの回答から欠落しているのは、 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'
1
Unapiedra