379 Stimmen

Wie kann ich Anfragen und die Antwort nachahmen?

Ich versuche, das Mockpaket von Python zu verwenden, um das Python requests Modul zu mocken. Was sind die grundlegenden Aufrufe, um mich in untenstehendem Szenario zum Laufen zu bringen?

In meiner views.py habe ich eine Funktion, die verschiedene requests.get() Aufrufe mit jeweils einer anderen Antwort macht

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

In meiner Testklasse möchte ich etwas Ähnliches tun, kann jedoch die genauen Methodenaufrufe nicht herausfinden

Schritt 1:

# Das requests Modul mocken
# Wenn mockedRequests.get('aurl') aufgerufen wird, dann 'a response' zurückgeben
# Wenn mockedRequests.get('burl') aufgerufen wird, dann 'b response' zurückgeben
# Wenn mockedRequests.get('curl') aufgerufen wird, dann 'c response' zurückgeben

Schritt 2:

Meine Ansicht aufrufen

Schritt 3:

Überprüfen, ob die Antwort 'a response', 'b response', 'c response' enthält

Wie kann ich Schritt 1 (Mocken des requests Moduls) abschließen?

33voto

WitoldW Punkte 739

Hier ist eine Lösung mit der Response-Klasse von Anfragen. Meiner Meinung nach ist es sauberer.

import json
from unittest.mock import patch
from requests.models import Response

def mocked_requests_get(*args, **kwargs):
    response_content = None
    request_url = kwargs.get('url', None)
    if request_url == 'aurl':
        response_content = json.dumps('a response')
    elif request_url == 'burl':
        response_content = json.dumps('b response')
    elif request_url == 'curl':
        response_content = json.dumps('c response')
    response = Response()
    response.status_code = 200
    response._content = str.encode(response_content)
    return response

@mock.patch('requests.get', side_effect=mocked_requests_get)
def test_fetch(self, mock_get):
     response = requests.get(url='aurl')
     assert ...

9voto

abkonsta Punkte 391

Ich habe mit Johannes Farhenkrugs Antwort hier angefangen, und es hat gut für mich funktioniert. Ich musste die requests-Bibliothek mocken, weil mein Ziel ist, meine Anwendung zu isolieren und keine Drittanbieter-Ressourcen zu testen.

Dann habe ich mehr über Pythons Mock-Bibliothek gelesen und festgestellt, dass ich die MockResponse-Klasse, die man einen 'Test Double' oder 'Fake' nennen könnte, durch eine python Mock-Klasse ersetzen kann.

Der Vorteil dabei ist der Zugriff auf Dinge wie assert_called_with, call_args und so weiter. Es sind keine zusätzlichen Bibliotheken erforderlich. Zusätzliche Vorteile wie 'Lesbarkeit' oder 'mehr Pythonic sein' sind subjektiv, daher können sie eine Rolle spielen oder auch nicht.

Hier ist meine Version, aktualisiert mit der Verwendung von Pythons Mock anstelle eines Test-Doubles:

import json
import requests
from unittest import mock

# Definition von Stubs
AUTH_TOKEN = '{"prop": "Value"}'
LIST_OF_WIDGETS = '{"widgets": ["widget1", "widget2"]}'
PURCHASED_WIDGETS = '{"widgets": ["purchased_widget"]}'

# Ausnahmeklasse, wenn eine unbekannte URL gemockt wird
class MockNotSupported(Exception):
  pass

# Factory-Methode, die die Mocks erstellt
def mock_requests_factory(response_stub: str, status_code: int = 200):
    return mock.Mock(**{
        'json.return_value': json.loads(response_stub),
        'text.return_value': response_stub,
        'status_code': status_code,
        'ok': status_code == 200
    })

# Nebeneffekt-Mock-Funktion
def mock_requests_post(*args, **kwargs):
    if args[0].endswith('/api/v1/get_auth_token'):
        return mock_requests_factory(AUTH_TOKEN)
    elif args[0].endswith('/api/v1/get_widgets'):
        return mock_requests_factory(LIST_OF_WIDGETS)
    elif args[0].endswith('/api/v1/purchased_widgets'):
        return mock_requests_factory(PURCHASED_WIDGETS)

    raise MockNotSupported

# requests.post patchen und Tests durchführen
with mock.patch('requests.post') as requests_post_mock:
  requests_post_mock.side_effect = mock_requests_post
  response = requests.post('https://myserver/api/v1/get_widgets')
  assert response.ok is True
  assert response.status_code == 200
  assert 'widgets' in response.json()

  # Jetzt kann ich auch Folgendes tun
  requests_post_mock.assert_called_with('https://myserver/api/v1/get_widgets')

Repl.it-Links:

https://repl.it/@abkonsta/Using-unittestMock-for-requestspost#main.py

https://repl.it/@abkonsta/Using-test-double-for-requestspost#main.py

8voto

Tom Chapin Punkte 2848

Wenn Sie eine gefälschte Antwort mocken möchten, können Sie dies auch einfach tun, indem Sie eine Instanz der Basisklasse HttpResponse erstellen, wie folgt:

von django.http.response import HttpResponseBase

self.fake_response = HttpResponseBase()

5voto

Sean DiZazzo Punkte 611

Dies hat für mich funktioniert, obwohl ich noch nicht viel komplizierte Tests gemacht habe.

import json
from requests import Response

class MockResponse(Response):
    def __init__(self,
                 url='http://example.com',
                 headers={'Content-Type':'text/html; charset=UTF-8'},
                 status_code=200,
                 reason = 'Erfolg',
                 _content = 'Hier steht etwas HTML',
                 json_ = None,
                 encoding='UTF-8'
                 ):
    self.url = url
    self.headers = headers
    if json_ and headers['Content-Type'] == 'application/json':
        self._content = json.dumps(json_).encode(encoding)
    else:
        self._content = _content.encode(encoding)

    self.status_code = status_code
    self.reason = reason
    self.encoding = encoding

Dann können Sie Antworten erstellen :

mock_response = MockResponse(
    headers={'Content-Type' :'application/json'},
    status_code=401,
    json_={'success': False},
    reason='Nicht autorisiert'
)
mock_response.raise_for_status()

gibt

requests.exceptions.HTTPError: 401 Client Error: Nicht autorisiert für URL: http://example.com

4voto

Ronald Theodoro Punkte 71

Eine mögliche Methode, um Anfragen zu umgehen, ist die Verwendung der Bibliothek Betamax. Sie zeichnet alle Anfragen auf und verwendet bei einer erneuten Anfrage an die gleiche URL mit denselben Parametern die aufgezeichnete Anfrage. Ich benutze sie, um einen Web-Crawler zu testen, und sie spart mir viel Zeit.

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/

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X