मैं अनुरोधों और प्रतिक्रिया का कैसे मजाक उड़ा सकता हूं?


221

मैं पायथन मॉड्यूल का मजाक उड़ाने के लिए पायथन मॉक पैकेज का उपयोग करने की कोशिश कर रहा हूं requests। मुझे नीचे के परिदृश्य में काम करने के लिए बुनियादी कॉल क्या हैं?

मेरे विचार में, मेरे पास एक फ़ंक्शन है जो विभिन्न प्रकार के अनुरोध करता है। () हर बार अलग-अलग प्रतिक्रिया के साथ कॉल करता है

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:

सत्यापन प्रतिक्रिया में 'एक प्रतिक्रिया', 'बी प्रतिक्रिया', 'सी प्रतिक्रिया' शामिल है

मैं चरण 1 को कैसे पूरा कर सकता हूं (अनुरोध मॉड्यूल का मजाक उड़ाते हुए)?


5
यहां काम कर रहे लिंक cra.mr/2014/05/20/mocking-requests-with-responses
योगेश लेले

जवाबों:


277

यह है कि आप इसे कैसे कर सकते हैं (आप इस फ़ाइल को इस प्रकार चला सकते हैं):

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

महत्वपूर्ण नोट: यदि आपकी MyGreatClassकक्षा एक अलग पैकेज में रहती है, तो कहें my.great.package, आपको my.great.package.requests.getकेवल 'request.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 __name__ == '__main__':
    unittest.main()

का आनंद लें!


2
MockResponse वर्ग एक महान विचार है! मैं एक resuests.Response वर्ग वस्तु नकली करने की कोशिश कर रहा था, लेकिन यह आसान नहीं था। मैं असली चीज़ के स्थान पर इस MockResponse का उपयोग कर सकता हूं। धन्यवाद!
योशी

@ योशी हाँ, मुझे अजगर के चारों ओर अपना सिर लपेटने में थोड़ा समय लगा लेकिन यह मेरे लिए बहुत अच्छा काम करता है!
जोहान्स फारेनक्रग

10
और पायथन 2.x में, बस के from unittest import mockसाथ बदलें import mockऔर बाकी के रूप में काम करता है। आपको mockपैकेज को अलग से स्थापित करने की आवश्यकता है ।
haridsv

3
बहुत खुबस। मुझे पायथन 3 में थोड़ा बदलाव करना पड़ा क्योंकि पाइथन 3 में mock_requests_getyieldreturn
पुनरावृत्तियों

1
यही सवाल मूल रूप से किस बारे में पूछ रहा था। मैंने तरीके निकाल लिए हैं (ऐप को पैकेज में पैक करें और कॉल करने के लिए एक test_client () पैक करें)। हालांकि इस पद के लिए धन्यवाद, अभी भी कोड की रीढ़ का उपयोग कर रहा था।
आत्महत्या बनी

142

प्रतिसाद लाइब्रेरी का उपयोग करके देखें । यहाँ उनके प्रलेखन से एक उदाहरण है :

import responses
import requests

@responses.activate
def test_simple():
    responses.add(responses.GET, 'http://twitter.com/api/1/foobar',
                  json={'error': 'not found'}, status=404)

    resp = requests.get('http://twitter.com/api/1/foobar')

    assert resp.json() == {"error": "not found"}

    assert len(responses.calls) == 1
    assert responses.calls[0].request.url == 'http://twitter.com/api/1/foobar'
    assert responses.calls[0].response.text == '{"error": "not found"}'

यह अपने आप को सभी नकली स्थापित करने पर काफी अच्छी सुविधा प्रदान करता है।

वहाँ भी HTTPretty है :

यह requestsपुस्तकालय के लिए विशिष्ट नहीं है , कुछ मायनों में और अधिक शक्तिशाली है, हालांकि मैंने पाया कि यह स्वयं अनुरोधों का निरीक्षण करने के लिए इतनी अच्छी तरह से उधार नहीं देता responsesहै , जो बहुत आसानी से करता है

इसमें नॉटमॉक भी है ।


एक नज़र में, मुझे responsesवाइल्डकार्ड url से मेल खाने का कोई तरीका नहीं दिखाई दिया - अर्थात, कॉलबैक लॉजिक को लागू करें जैसे "url का अंतिम भाग लें, इसे मैप में देखें और संबंधित मान वापस करें"। क्या यह संभव है, और मैं इसे याद कर रहा हूं?
स्क्यूबो

1
@scubbo आप url param के रूप में एक पूर्व-संकलित regex पास कर सकते हैं और कॉलबैक शैली का उपयोग कर सकते हैं github.com/getsentry/responses#dynamic-responses यह आपको वह वाइल्डकार्ड व्यवहार देगा जो मैं चाहता हूं कि मुझे लगता है ( requestarg। कॉलबैक
फंक

48

यहाँ मेरे लिए क्या काम किया गया है:

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

3
यदि आप पाठ / html प्रतिक्रियाओं की अपेक्षा कर रहे हैं तो यह काम करेगा। यदि आप एक REST एपीआई का मजाक उड़ा रहे हैं, तो स्टेटस कोड आदि की जांच करना चाहते हैं, तो जोहान्स [ stackoverflow.com/a/28507806/3559967] से जवाब शायद जाने का रास्ता है।
एंटनी

5
पायथन 3 के लिए, का उपयोग करें from unittest import mockdocs.python.org/3/library/unittest.mock.html
फ़ीनिक्स

33

मैंने अलग मॉड्यूल के लिए परीक्षण लिखने के लिए अनुरोध-नकली का उपयोग किया :

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

आप कहां से मिलेंगे '(स्वयं, एम):'
डेनिस एवसेव

16

यह है कि आप अनुरोधों को कैसे मॉक करते हैं, इसे अपने 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

1
यदि मैं किसी समारोह का मजाक बनाना चाहता हूं तो क्या होगा? उदाहरण के लिए इसे कैसे मॉक करें: mockresponse.json () = {"key": "value"}
primoz

1
: @primoz, मैं उस के लिए एक गुमनाम समारोह / लैम्ब्डा इस्तेमाल कियाmockresponse.json = lambda: {'key': 'value'}
टेलर

1
याmockresponse.json.return_value = {"key": "value"}
लार्स ब्लमबर्ग

5

यदि आप एक नकली प्रतिक्रिया का मखौल उड़ाना चाहते हैं, तो ऐसा करने का एक और तरीका बस बेस HttpResponse वर्ग के एक उदाहरण को इंस्टेंट करना है, जैसे:

from django.http.response import HttpResponseBase

self.fake_response = HttpResponseBase()

यह वह उत्तर है जो मैं खोजने की कोशिश कर रहा था: एक नकली django प्रतिक्रिया ऑब्जेक्ट प्राप्त करें जो इसे लगभग ई 2 ई परीक्षण के लिए मिडलवेयर के सरगम ​​के माध्यम से बना सकता है। HttpResponse, बजाय ... बेस, हालांकि मेरे लिए चाल चली। धन्यवाद!
low_ghost

4

अनुरोधों के आसपास काम करने का एक संभावित तरीका लाइब्रेरी बिटमैक्स का उपयोग कर रहा है, यह सभी अनुरोधों को रिकॉर्ड करता है और उसके बाद यदि आप एक ही यूआरएल में एक ही पैरामीटर के साथ एक अनुरोध करते हैं तो बिटमैक्स रिकॉर्ड किए गए अनुरोध का उपयोग करेगा, मैं इसका उपयोग वेब क्रॉलर का परीक्षण करने के लिए कर रहा हूं और यह मुझे बहुत समय बचाता है।

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/


ध्यान दें कि बिटमैक्स केवल अनुरोधों के साथ काम करने के लिए डिज़ाइन किया गया है , यदि आपको HTTP अनुरोधों को निम्न स्तर के HTTP API जैसे कि plplib3 , या वैकल्पिक aiohttp के साथ , या क्लाइंट लिबास जैसे बोटो ... को कैप्चर करने की आवश्यकता है, तो इसके बजाय vcrpy का उपयोग करें जो निचले स्तर पर काम करता है। पर अधिक github.com/betamaxpy/betamax/issues/125
Le Hibou

0

बस उन लोगों के लिए एक उपयोगी संकेत है जो अभी भी संघर्ष कर रहे हैं, urllib या urllib2 / urllib3 से अनुरोधों के लिए परिवर्तित कर रहे हैं और एक प्रतिक्रिया का मजाक उड़ाने की कोशिश कर रहे हैं- मुझे अपने मॉक को लागू करते समय थोड़ी भ्रमित करने वाली त्रुटि मिल रही थी:

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

गुण:

ठीक है, निश्चित रूप से, अगर मुझे कुछ भी पता है कि कैसे withकाम करता है (मैंने नहीं किया था), मुझे पता था कि यह एक उल्टी, अनावश्यक संदर्भ ( पीईपी 343 से ) था। अनुरोध लाइब्रेरी का उपयोग करते समय अनावश्यक क्योंकि यह हुड के नीचे मूल रूप से आपके लिए एक ही काम करता है । बस को हटा दें withऔर नंगे requests.get(...)और बॉब के अपने चाचा का उपयोग करें


0

मैं इस जानकारी को जोड़ दूंगा क्योंकि मेरे पास एक कठिन समय था जो यह बताता है कि एक async एपीआई कॉल को कैसे मॉक करें।

यहाँ मैं एक async कॉल नकली करने के लिए क्या किया है।

यहां वह फ़ंक्शन है जो मैं परीक्षण करना चाहता था

async def get_user_info(headers, payload):
    return await httpx.AsyncClient().post(URI, json=payload, headers=headers)

आपको अभी भी MockResponse वर्ग की आवश्यकता है

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

आप MockResponseAsync वर्ग जोड़ें

class MockResponseAsync:
    def __init__(self, json_data, status_code):
        self.response = MockResponse(json_data, status_code)

    async def getResponse(self):
        return self.response

यहाँ परीक्षण है। यहाँ महत्वपूर्ण बात यह है कि मैं प्रतिक्रिया बना रहा हूँ क्योंकि यह init function async नहीं हो सकता है और getResponse पर कॉल करना async है, इसलिए यह सब जाँच लिया गया है।

@pytest.mark.asyncio
@patch('httpx.AsyncClient')
async def test_get_user_info_valid(self, mock_post):
    """test_get_user_info_valid"""
    # Given
    token_bd = "abc"
    username = "bob"
    payload = {
        'USERNAME': username,
        'DBNAME': 'TEST'
    }
    headers = {
        'Authorization': 'Bearer ' + token_bd,
        'Content-Type': 'application/json'
    }
    async_response = MockResponseAsync("", 200)
    mock_post.return_value.post.return_value = async_response.getResponse()

    # When
    await api_bd.get_user_info(headers, payload)

    # Then
    mock_post.return_value.post.assert_called_once_with(
        URI, json=payload, headers=headers)

यदि आपके पास ऐसा करने का एक बेहतर तरीका है जो मुझे बताता है, लेकिन मुझे लगता है कि यह बहुत साफ है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.