web-dev-qa-db-fra.com

Comment puis-je simuler des demandes et la réponse?

J'essaie d'utiliser le paquet fictif Pythons pour ficher le module requests Pythons Quels sont les appels de base pour me faire travailler dans le scénario ci-dessous?

Dans mon views.py, j'ai une fonction qui fait une variété d'appels requests.get () avec une réponse différente à chaque fois

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

Dans ma classe de test, je veux faire quelque chose comme ceci mais je ne peux pas comprendre les appels de méthode exacts

Étape 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'

Étape 2:

Appelle ma vue

Étape 3: 

vérifier que la réponse contient 'une réponse', 'réponse b', 'réponse c'

Comment puis-je terminer l'étape 1 (mocker le module de demandes)?

127
kk1957

Voici ce qui a fonctionné pour moi:

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

Voici comment vous pouvez le faire (vous pouvez exécuter ce fichier tel quel):

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()

Remarque importante: Si votre classe MyGreatClass réside dans un package différent, par exemple my.great.package, vous devez simuler my.great.package.requests.get au lieu de simplement 'request.get'. Dans ce cas, votre cas de test ressemblerait à ceci:

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()

Prendre plaisir!

178
Johannes Fahrenkrug

Essayez d’utiliser la bibliothèque de réponses :

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"}'

fournit tout à fait une bonne commodité sur la mise en place de tout le moqueur

Il y a aussi HTTPretty :

ce n'est pas spécifique à la bibliothèque requests, plus puissante à certains égards bien que j'ai constaté qu'elle ne se prêtait pas si bien à l'inspection des requêtes interceptées, ce que responses fait assez facilement

et httmock :

65
Anentropic

J'ai utilisé request-mock pour écrire des tests pour un module distinct:

# module.py
import requests

class A():

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

Et les tests:

# 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()
22
AnaPana

voici comment vous vous moquez de requests.post, changez-le en votre méthode 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
8
tingyiy

Si vous voulez simuler une fausse réponse, vous pouvez également instancier une instance de la classe de base HttpResponse, comme ceci:

from Django.http.response import HttpResponseBase

self.fake_response = HttpResponseBase()
2
Tom Chapin

Une solution possible pour contourner les requêtes consiste à utiliser la bibliothèque betamax. Elle enregistre toutes les requêtes. Par la suite, si vous faites une requête dans la même URL avec les mêmes paramètres, le betamax utilisera la requête enregistrée. Je l'ai utilisé pour tester le crawler Web. et cela m'a fait gagner beaucoup de temps.

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/

0
Ronald Theodoro