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?

4voto

Jack Deeth Punkte 2768

Kannst du stattdessen requests-mock verwenden?

Angenommen, deine Funktion myview nimmt stattdessen ein requests.Session-Objekt an, macht Anfragen damit und verarbeitet die Ausgabe:

# mypackage.py
def myview(session):
    res1 = session.get("http://aurl")
    res2 = session.get("http://burl")
    res3 = session.get("http://curl")
    return f"{res1.text}, {res2.text}, {res3.text}"

# test_myview.py
from mypackage import myview
import requests

def test_myview(requests_mock):
    # set up requests
    a_req = requests_mock.get("http://aurl", text="a response")
    b_req = requests_mock.get("http://burl", text="b response")
    c_req = requests_mock.get("http://curl", text="c response")

    # test myview behaviour
    session = requests.Session()
    assert myview(session) == "a response, b response, c response"

    # check that requests weren't called repeatedly
    assert a_req.called_once
    assert b_req.called_once
    assert c_req.called_once
    assert requests_mock.call_count == 3

Du kannst requests_mock auch mit Frameworks verwenden, die nicht Pytest sind - die Dokumentation ist großartig.

3voto

muTheTechie Punkte 1070

Die Verwendung von requests_mock ist einfach, um Anfragen zu patchen

pip install requests-mock

from unittest import TestCase
import requests_mock
from  import  (auth)

class TestApi(TestCase):
  @requests_mock.Mocker()
  def test_01_authentication(self, m):
        """Erfolgreiche Authentifizierung mittels Benutzername und Passwort"""
        token = 'token'
        m.post(f'http://localhost/auth', json= {'token': token})
        act_token = auth("user", "pass")
        self.assertEqual(act_token, token)

2voto

Martbob Punkte 461

Ich werde diese Informationen hinzufügen, da ich Schwierigkeiten hatte, herauszufinden, wie man einen asynchronen API-Aufruf nachahmt.

Hier ist, was ich getan habe, um einen asynchronen Aufruf zu vermocken.

Hier ist die Funktion, die ich testen wollte

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

Sie benötigen immer noch die MockResponse-Klasse

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

Sie fügen die MockResponseAsync-Klasse hinzu

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

    async def getResponse(self):
        return self.response

Hier ist der Test. Das Wichtige hier ist, dass ich die Antwort zuerst erstelle, da die init-Funktion nicht asynchron sein kann und der Aufruf von getResponse asynchron ist, sodass alles überprüft wurde.

@pytest.mark.asyncio
@patch('httpx.AsyncClient')
async def test_get_user_info_valid(self, mock_post):
    """test_get_user_info_valid"""
    # Gegeben
    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()

    # Wenn
    await api_bd.get_user_info(headers, payload)

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

Wenn Sie eine bessere Möglichkeit haben, das zu tun, lassen Sie es mich wissen, aber ich denke, es ist ziemlich sauber so.

2voto

wowkin2 Punkte 5022

Der einfachste Weg bisher:

from unittest import TestCase
from unittest.mock import Mock, patch

from .utils import method_foo

class TestFoo(TestCase):

    @patch.object(utils_requests, "post")  # hier gewünschte Methode ändern
    def test_foo(self, mock_requests_post):
        # ERKLÄRUNG: Die oben gemockte Methode 'post' wird einen eingebauten Mock zurückgeben,
        # und deren Methode 'json' wird den Mock 'mock_data' zurückgeben,
        # der das Argument 'return_value' mit unseren zurückzugebenden Daten hat
        mock_data = Mock(return_value=[{"id": 1}, {"id": 2}])
        mock_requests_post.return_value.json = mock_data

        method_foo()

        # TODO: hier Asserts einfügen

"""
Beispiel einer Methode, die Sie in utils.py testen können
"""
def method_foo():
    response = requests.post("http://example.com")
    records = response.json()
    for record in records:
        print(record.get("id"))
        # hier weitere Aktionen durchführen

2voto

ruslan_krivoshein Punkte 250

Für diejenigen, die keine zusätzlichen Bibliotheken für pytest installieren möchten, gibt es ein Beispiel hier. Ich werde es hier mit einer Erweiterung basierend auf den obigen Beispielen duplizieren:

import datetime

import requests

class MockResponse:
    def __init__(self, json_data, status_code):
        self.json_data = json_data
        self.status_code = status_code
        self.elapsed = datetime.timedelta(seconds=1)

    # mock json() method always returns a specific testing dictionary
    def json(self):
        return self.json_data

def test_get_json(monkeypatch):
    # Es können beliebige Argumente übergeben und mock_get() gibt immer unser gemocktes Objekt zurück, das nur die .json() Methode hat.
    def mock_get(*args, **kwargs):
        return MockResponse({'mock_key': 'mock_value'}, 418)

    # Das monkeypatch für requests.get auf mock_get anwenden
    monkeypatch.setattr(requests, 'get', mock_get)

    # app.get_json, das requests.get enthält, verwendet das monkeypatch
    response = requests.get('https://fakeurl')
    response_json = response.json()

    assert response_json['mock_key'] == 'mock_value'
    assert response.status_code == 418
    assert response.elapsed.total_seconds() == 1

============================= Testsession startet ==============================
sammeln ... gesammelt 1 Artikel

test_so.py::test_get_json BESTANDEN                                          [100%]

============================== 1 bestanden in 0.07s ===============================

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