web-dev-qa-db-ja.com

リクエストとレスポンスをモックするにはどうすればよいですか?

Pythonsモックパッケージ を使用して、Pythons requestsモジュールをモックしようとしています。以下のシナリオで作業するための基本的な呼び出しは何ですか?

私のviews.pyには、毎回異なる応答でさまざまなrequests.get()を呼び出す関数があります

def myview(request):
  res1 = requests.get('aurl')
  res2 = request.get('burl')
  res3 = request.get('curl')

私のテストクラスでは、このようなことをしたいのですが、正確なメソッド呼び出しを把握できません

ステップ1:

# Mock the requests module
# when mockedRequests.get('aurl') is called then return 'a response'
# when mockedRequests.get('burl') is called then return 'b response'
# when mockedRequests.get('curl') is called then return 'c response'

ステップ2:

私の意見を呼ぶ

ステップ3:

応答に「a応答」、「b応答」、「c応答」が含まれていることを確認します

ステップ1(要求モジュールのモック)を完了するにはどうすればよいですか?

147
kk1957

ここに私のために働いたものがあります:

import mock
@mock.patch('requests.get', mock.Mock(side_effect = lambda k:{'aurl': 'a response', 'burl' : 'b response'}.get(k, 'unhandled request %s'%k)))
36
kk1957

これはそれを行う方法です(このファイルをそのまま実行できます)。

import requests
import unittest
from unittest import mock

# This is the class we want to test
class MyGreatClass:
    def fetch_json(self, url):
        response = requests.get(url)
        return response.json()

# This method will be used by the mock to replace requests.get
def mocked_requests_get(*args, **kwargs):
    class MockResponse:
        def __init__(self, json_data, status_code):
            self.json_data = json_data
            self.status_code = status_code

        def json(self):
            return self.json_data

    if args[0] == 'http://someurl.com/test.json':
        return MockResponse({"key1": "value1"}, 200)
    Elif args[0] == 'http://someotherurl.com/anothertest.json':
        return MockResponse({"key2": "value2"}, 200)

    return MockResponse(None, 404)

# Our test case class
class MyGreatClassTestCase(unittest.TestCase):

    # We patch 'requests.get' with our own method. The mock object is passed in to our test case method.
    @mock.patch('requests.get', side_effect=mocked_requests_get)
    def test_fetch(self, mock_get):
        # Assert requests.get calls
        mgc = MyGreatClass()
        json_data = mgc.fetch_json('http://someurl.com/test.json')
        self.assertEqual(json_data, {"key1": "value1"})
        json_data = mgc.fetch_json('http://someotherurl.com/anothertest.json')
        self.assertEqual(json_data, {"key2": "value2"})
        json_data = mgc.fetch_json('http://nonexistenturl.com/cantfindme.json')
        self.assertIsNone(json_data)

        # We can even assert that our mocked method was called with the right parameters
        self.assertIn(mock.call('http://someurl.com/test.json'), mock_get.call_args_list)
        self.assertIn(mock.call('http://someotherurl.com/anothertest.json'), mock_get.call_args_list)

        self.assertEqual(len(mock_get.call_args_list), 3)

if __== '__main__':
    unittest.main()

重要な注意:MyGreatClassクラスが別のパッケージ(my.great.packageなど)にある場合、単に 'request.get'ではなくmy.great.package.requests.getをモックする必要があります。その場合、テストケースは次のようになります。

import unittest
from unittest import mock
from my.great.package import MyGreatClass

# This method will be used by the mock to replace requests.get
def mocked_requests_get(*args, **kwargs):
    # Same as above


class MyGreatClassTestCase(unittest.TestCase):

    # Now we must patch 'my.great.package.requests.get'
    @mock.patch('my.great.package.requests.get', side_effect=mocked_requests_get)
    def test_fetch(self, mock_get):
        # Same as above

if __== '__main__':
    unittest.main()

楽しい!

192

私は requests-mock を使用して、個別のモジュールのテストを作成しました。

# module.py
import requests

class A():

    def get_response(self, url):
        response = requests.get(url)
        return response.text

そしてテスト:

# tests.py
import requests_mock
import unittest

from module import A


class TestAPI(unittest.TestCase):

    @requests_mock.mock()
    def test_get_response(self, m):
        a = A()
        m.get('http://aurl.com', text='a response')
        self.assertEqual(a.get_response('http://aurl.com'), 'a response')
        m.get('http://burl.com', text='b response')
        self.assertEqual(a.get_response('http://burl.com'), 'b response')
        m.get('http://curl.com', text='c response')
        self.assertEqual(a.get_response('http://curl.com'), 'c response')

if __== '__main__':
    unittest.main()
24
AnaPana

これはrequests.postをモックする方法で、httpメソッドに変更します

@patch.object(requests, 'post')
def your_test_method(self, mockpost):
    mockresponse = Mock()
    mockpost.return_value = mockresponse
    mockresponse.text = 'mock return'

    #call your target method now
13
tingyiy

偽の応答をモックしたい場合、別の方法として、ベースHttpResponseクラスのインスタンスを次のように単純にインスタンス化する方法があります。

from Django.http.response import HttpResponseBase

self.fake_response = HttpResponseBase()
2
Tom Chapin

リクエストを回避する方法の1つは、ライブラリbetamaxを使用することです。すべてのリクエストを記録します。その後、同じパラメーターで同じURLでリクエストを行うと、betamaxは記録されたリクエストを使用し、Webクローラーをテストしますそしてそれは私に多くの時間を節約します。

import os

import requests
from betamax import Betamax
from betamax_serializers import pretty_json


WORKERS_DIR = os.path.dirname(os.path.abspath(__file__))
CASSETTES_DIR = os.path.join(WORKERS_DIR, u'resources', u'cassettes')
MATCH_REQUESTS_ON = [u'method', u'uri', u'path', u'query']

Betamax.register_serializer(pretty_json.PrettyJSONSerializer)
with Betamax.configure() as config:
    config.cassette_library_dir = CASSETTES_DIR
    config.default_cassette_options[u'serialize_with'] = u'prettyjson'
    config.default_cassette_options[u'match_requests_on'] = MATCH_REQUESTS_ON
    config.default_cassette_options[u'preserve_exact_body_bytes'] = True


class WorkerCertidaoTRT2:
    session = requests.session()

    def make_request(self, input_json):
        with Betamax(self.session) as vcr:
            vcr.use_cassette(u'google')
            response = session.get('http://www.google.com')

https://betamax.readthedocs.io/en/latest/

1
Ronald Theodoro

まだ苦労している人々への有用なヒントであり、urllibまたはurllib2/urllib3からリクエストに変換し、応答をモックしようとしています-モックを実装するときに少し混乱するエラーが発生していました:

with requests.get(path, auth=HTTPBasicAuth('user', 'pass'), verify=False) as url:

AttributeError:__enter__

もちろん、withの動作について知っていた(しなかった)場合、それは痕跡の、不必要なものであることがわかります context (from PEP 34 ) 。要求ライブラリを使用する場合、基本的に同じことを行うため、不要です 内部withを削除し、裸のrequests.get(...)および Bob's your uncle を使用するだけです。

0
Max P Magee