Zum Hauptinhalt springen
Wenn ein Paket eine externe API nutzen soll, müssen zwei Dinge gleichzeitig erfüllt sein: die von Laravel verwaltete Konfiguration des HTTP-Clients übernehmen und keine Einstellungen eines Aufrufs in den nächsten mitnehmen. Wenn Sie eine eigene Factory erzeugen oder Anfragen vor dem Senden lange vorhalten, kann das dazu führen, dass Fakes der nutzenden Anwendung nicht greifen. Diese Seite basiert auf der Implementierung von Laravel Framework v13.39.0. Die Grundlagen finden Sie unter HTTP-Client, die Vorbereitung der Testumgebung unter Pakete testen.

Die Factory teilen, den PendingRequest jedes Mal neu erstellen

Der HTTP-Client von Laravel besteht aus Objekten mit unterschiedlichen Verantwortlichkeiten. FoundationServiceProvider registriert die Factory als Singleton, und auch die Fassade Http löst über denselben Klassennamen aus dem Container auf. Deshalb lässt sich in einer gewöhnlichen Laravel-Anwendung auch die Kommunikation über eine aus dem Container bezogene Factory mit Http::fake() überprüfen. Ein Entwurf, der die Fassade direkt nutzt, ist ebenfalls möglich, doch auf dieser Seite wird die Factory injiziert, um die Abhängigkeit explizit zu machen. Gewöhnliche Methoden zur Anfragekonfiguration wie baseUrl() auf der Factory werden über __call() an einen neuen PendingRequest weitergeleitet. createPendingRequest() übergibt die zum Zeitpunkt der Erzeugung gültigen Fake-Callbacks und die Einschränkung für Stray Requests, und newPendingRequest() wendet die gemeinsame Middleware und die gemeinsamen Optionen an.
Wenn Sie im Paket new Factory() oder einen Client einer anderen HTTP-Bibliothek erzeugen, werden die Fakes der gemeinsamen Factory und Http::preventStrayRequests() nicht übernommen. Diese Funktionen blockieren nicht die gesamte Kommunikation des Prozesses. Verwenden Sie für Clients, die für Laravel gedacht sind, die gemeinsame Factory, und stellen Sie bei einem unabhängigen Client eine eigene Testmethode für diesen Client bereit.

API-Operationen in einem kleinen Client bündeln

Als Beispiel dient das Paket Acme\Catalog, das Produkte aus einer fiktiven Katalog-API abruft. src/CatalogClient.php hält nur die Factory und die Basis-URL, und die Anfrage wird erst während der Ausführung von product() erstellt.
Bei diesem Entwurf bleibt der im vorherigen Aufruf hinzugefügte Accept-Language nicht erhalten, wenn Sie mit demselben Client nach product(42, 'ja') noch product(42) aufrufen. Gemeinsame Einstellungen, die die nutzende Anwendung an der Factory vorgenommen hat, werden weiterhin auf jede Anfrage angewendet.
  • Paketspezifische Einstellungen wenden Sie auf den neu erstellten PendingRequest an. Vermeiden Sie es, bei der Initialisierung des Pakets Http::globalOptions() zu setzen, da dies auch unbeteiligte Kommunikation beeinflusst.
  • Im Konstruktor wird nichts gesendet. Entwerfen Sie das Paket so, dass das bloße Auflösen des Service, das Starten von Artisan oder das Veröffentlichen der Konfigurationsdatei keine Verbindung zur externen API herstellt.
  • throw() macht aus 4xx und 5xx eine RequestException. Unterscheiden Sie diese von der ConnectionException bei Verbindungsfehlern oder Timeouts, und ersetzen Sie Fehler nicht durch Erfolgswerte wie ein leeres Array.
  • json(flags: JSON_THROW_ON_ERROR) macht aus ungültigem JSON eine JsonException. Das Beispiel prüft nur, ob ein Array vorliegt, daher sollten Sie bei einer echten API auch Pflichtfelder und Datentypen validieren.
Wenn es nur darum geht, Basis-URL oder Header zu vereinheitlichen, sind auch die HTTP-Makros aus der offiziellen Dokumentation eine Option. Wenn das Paket API-Operationen, die Prüfung von Rückgabewerten und den Exception-Vertrag bereitstellt, ist es wartungsfreundlicher, dies wie hier in eine eigene Klasse auszulagern. Wie Makros funktionieren, erfahren Sie unter Macroable-Trait.

Im Service-Provider registrieren

Legen Sie in config/catalog.php des Pakets die Standardkonfiguration an. Speichern Sie hier keine HTTP-Clients oder Closures, sondern nur Werte, die im Konfigurations-Cache abgelegt werden können.
src/CatalogServiceProvider.php führt die Konfiguration zusammen und übergibt beim Auflösen des Clients die gemeinsame Factory. Das Binding der Factory selbst wird nicht überschrieben.
Der Provider wird zum Beispiel über die Paket-Erkennung geladen. Nutzer veröffentlichen die Konfiguration und ändern die Basis-URL in config/acme-catalog.php auf die tatsächliche API. catalog.example.test im Beispiel ist ein fiktiver Host für Erklärungen und Fakes.
Auf der nutzenden Seite wird der Client per Konstruktor-Injektion oder über den Container aufgelöst. Erst mit diesem Aufruf wird die Anfrage zum Abrufen des Produkts gesendet.
Für die Registrierung wird bind() verwendet, doch wenn das Objekt, das den Client hält, ein Singleton ist, wird auch diese Referenz nicht jedes Mal neu erzeugt. Entscheidend ist, aufrufspezifische Sprache, Zugangsdaten und Anfrageobjekte nicht in Eigenschaften zu speichern. Details finden Sie unter Lebenszyklus von Paket-Services entwerfen. Das Überschreiben der Konfiguration und das Aktualisieren des Caches beim Deployment werden unter Zusammenführen und Cachen der Paketkonfiguration behandelt. Verwalten Sie die Basis-URL als vertrauenswürdige Anwendungskonfiguration, und lassen Sie das Verbindungsziel nicht frei über Eingaben der Anfrage festlegen.

Den echten Client mit Fakes überprüfen

Wenn Sie den Client selbst mocken, können Sie nicht prüfen, ob er die gemeinsame Factory nutzt und URL und Header korrekt zusammensetzt. In den Tests des Pakets lösen Sie den echten CatalogClient aus dem Container auf und ersetzen nur die HTTP-Antworten durch Fakes. Das folgende Beispiel läuft in der Testumgebung von Orchestra Testbench. In setUp() jedes Tests wird preventStrayRequests() aktiviert. In Tests mit Kommunikation legen Sie nur für die erwartete URL eine Antwort fest und verzichten auf einen '*'-Fallback, sodass auch fehlerhafte URLs erkannt werden.
Im zweiten Test wird der Fake erst nach dem Auflösen des Clients festgelegt. Da der Client die Factory hält und den PendingRequest erst beim tatsächlichen Aufruf erzeugt, greift der Fake auch in dieser Reihenfolge. Würde der Entwurf so geändert, dass der PendingRequest im Konstruktor erzeugt und gehalten wird, erkennt dieser Test, dass ein danach hinzugefügter Fake nicht greift. assertSent() prüft, dass mindestens eine passende Anfrage existiert. Kombinieren Sie es mit assertSentCount(), damit unbeabsichtigte zusätzliche Anfragen nicht übersehen werden. assertSentInOrder() prüft Anzahl und Reihenfolge und eignet sich daher auch, um die Zustandstrennung bei aufeinanderfolgender Nutzung desselben Clients zu überprüfen. Der Schutz wird nach parent::setUp() eingerichtet. Mit diesem Test allein lässt sich nicht auch die Kommunikation während des Starts der Provider überprüfen, halten Sie daher an einem Entwurf fest, der bei Registrierung und Start nicht kommuniziert.
Mit Fakes lassen sich die vom Client zusammengesetzte Anfrage und die Verarbeitung der vorbereiteten Antworten prüfen. Authentifizierung beim echten Dienst, TLS-Verbindungen, tatsächliche Timeout-Dauern und Spezifikationsänderungen der externen API werden dadurch nicht garantiert. Überprüfungen mit externen Verbindungen führen Sie separat in einer ausdrücklich dafür vorbereiteten Testumgebung durch.

Zusätzliche Tests für die langfristige Wartung

Wenn Sie die Verantwortlichkeiten der gemeinsamen Factory und der einzelnen PendingRequests trennen, können Nutzer die gewohnten Testmethoden von Laravel unverändert verwenden. Statt zuerst einen eigenen Fake-Mechanismus zu bauen, sollten Sie eine Implementierung bereitstellen, die über die gemeinsame Factory läuft, sowie Tests, die dies bestätigen.

Verwendete Primärquellen

Zuletzt geändert am 27. Oktober 2026