Naar hoofdinhoud gaan
Wanneer je vanuit een package een externe API wilt gebruiken, combineer je twee dingen: de configuratie van de door Laravel beheerde HTTP-client overnemen en geen instellingen van de ene aanroep naar de volgende meenemen. Als je zelf een Factory aanmaakt of een nog niet verzonden request lange tijd vasthoudt, kan dat ertoe leiden dat de fakes van de gebruikende kant niet worden toegepast. Deze pagina is gecontroleerd aan de hand van de implementatie van Laravel Framework v13.39.0. Zie HTTP-client voor de basisbediening en Packages testen voor het opzetten van de testomgeving.

Deel de Factory en maak elke keer een nieuwe PendingRequest

De HTTP-client van Laravel bestaat uit objecten met verschillende verantwoordelijkheden. FoundationServiceProvider registreert de Factory als singleton, en ook de Http-facade resolvet uit de container via dezelfde klassenaam. Daarom kun je in een gewone Laravel-applicatie ook de communicatie via een Factory die je uit de container ontvangt, verifiëren met Http::fake(). Een ontwerp dat de facade direct gebruikt is ook mogelijk, maar op deze pagina injecteren we de Factory om de afhankelijkheden expliciet te maken. Gewone methoden voor requestinstellingen op de Factory, zoals baseUrl(), worden via __call() doorgestuurd naar een nieuwe PendingRequest. createPendingRequest() geeft de fake-callbacks en de beperking op stray requests mee zoals die op het moment van aanmaken gelden, en newPendingRequest() past de gedeelde middleware en gedeelde opties toe.
Als je binnen een package new Factory() of een client van een andere HTTP-bibliotheek aanmaakt, worden de fakes en Http::preventStrayRequests() van de gedeelde Factory niet overgenomen. Deze functies blokkeren niet alle communicatie van het hele proces. Gebruik voor clients die voor Laravel bedoeld zijn de gedeelde Factory, en zorg voor een eigen testmethode als je een onafhankelijke client aanbiedt.

API-bewerkingen bundelen in een kleine client

Als voorbeeld nemen we het package Acme\Catalog, dat producten ophaalt uit een fictieve catalogus-API. src/CatalogClient.php houdt alleen de Factory en de basis-URL vast, en het request wordt pas aangemaakt tijdens de uitvoering van product().
Met dit ontwerp blijft de Accept-Language die bij de vorige aanroep is toegevoegd niet hangen, ook als je met dezelfde client eerst product(42, 'ja') en daarna product(42) aanroept. De gedeelde instellingen die de gebruikende applicatie op de Factory heeft ingesteld, worden nog steeds op elk request toegepast.
  • Pas packagespecifieke instellingen toe op de nieuw aangemaakte PendingRequest. Vermijd het instellen van Http::globalOptions() tijdens de initialisatie van het package, omdat dat ook ongerelateerde communicatie beïnvloedt.
  • Verzend niets in de constructor. Ontwerp het zo dat het resolven van de service, het starten van Artisan of het publiceren van configuratiebestanden op zichzelf geen verbinding met de externe API maakt.
  • throw() maakt van 4xx- en 5xx-responses een RequestException. Onderscheid dit van een ConnectionException bij verbindingsfouten of timeouts, en vervang een fout niet door een succeswaarde zoals een lege array.
  • json(flags: JSON_THROW_ON_ERROR) maakt van ongeldige JSON een JsonException. In het voorbeeld wordt alleen gecontroleerd of het een array is, dus valideer bij een echte API ook de verplichte velden en datatypen.
Als je alleen de basis-URL of headers wilt delen, zijn de HTTP-macro’s uit de officiële documentatie ook een optie. Als je als package ook API-bewerkingen, de controle van returnwaarden en het contract voor exceptions aanbiedt, is het makkelijker te onderhouden als je dit in een aparte klasse zoals deze onderbrengt. Zie De Macroable-trait voor hoe macro’s werken.

Registreren in de service provider

Zet de standaardinstellingen klaar in config/catalog.php van het package. Sla hier geen HTTP-clients of closures op, maar alleen waarden die in de configuratiecache kunnen worden opgeslagen.
src/CatalogServiceProvider.php merget de configuratie en geeft de gedeelde Factory mee wanneer de client wordt geresolved. De binding van de Factory zelf wordt niet overschreven.
De provider wordt geladen via bijvoorbeeld Package discovery. De gebruiker publiceert de configuratie en wijzigt de basis-URL in config/acme-catalog.php naar de echte API. De catalog.example.test in het voorbeeld is een fictieve host voor de uitleg en voor fakes.
Aan de gebruikende kant resolve je de client via constructor-injectie of de container. Pas bij deze aanroep wordt het request voor het ophalen van het product verzonden.
Voor de registratie wordt bind() gebruikt, maar als de kant die de client vasthoudt een singleton is, wordt die referentie niet elke keer opnieuw aangemaakt. Het is belangrijk om de taal, de authenticatiegegevens en het requestobject die specifiek zijn voor een aanroep niet in properties op te slaan. Zie Levenscyclusontwerp van packageservices voor details. Het overschrijven van de configuratie en het vernieuwen van de cache bij een deploy worden behandeld in Configuratie van packages mergen en cachen. Beheer de basis-URL als betrouwbare applicatieconfiguratie en laat de bestemming niet vrij bepalen door de invoer van een request.

De echte client verifiëren met fakes

Als je de client zelf mockt, kun je niet controleren of de gedeelde Factory wordt gebruikt en of URL’s en headers correct worden opgebouwd. In de tests van het package resolve je de echte CatalogClient uit de container en fake je alleen de HTTP-responses. Het volgende voorbeeld draait in de testomgeving van Orchestra Testbench. In setUp() van elke test wordt preventStrayRequests() ingeschakeld. In tests die communiceren stel je alleen voor de verwachte URL een response in en laat je een '*'-fallback weg, zodat ook fouten in de URL worden gedetecteerd.
In de tweede test wordt de fake pas ingesteld nadat de client is geresolved. Omdat de client de Factory vasthoudt en de PendingRequest pas bij de daadwerkelijke aanroep aanmaakt, wordt de fake ook in deze volgorde toegepast. Als je het ontwerp wijzigt zodat de constructor een PendingRequest aanmaakt en vasthoudt, detecteert deze test dat fakes die daarna zijn toegevoegd niet worden toegepast. assertSent() verifieert dat er minstens één overeenkomend request is. Combineer dit met assertSentCount() om onbedoelde extra verzendingen niet te missen. assertSentInOrder() verifieert het aantal en de volgorde, dus je kunt het ook gebruiken om de scheiding van status te controleren wanneer je dezelfde client meerdere keren achter elkaar gebruikt. De guard wordt ingesteld na parent::setUp(). Deze test alleen kan dus geen communicatie tijdens het opstarten van de provider verifiëren, dus houd vast aan een ontwerp waarin registratie en opstarten geen communicatie veroorzaken.
Met fakes kun je de requests controleren die de client opbouwt, en de verwerking van de responses die je hebt klaargezet. Ze garanderen niets over de authenticatie van de echte service, TLS-verbindingen, werkelijke timeoutduur of wijzigingen in de specificatie van de externe API. Voer verificaties met externe verbindingen apart uit in een expliciet daarvoor ingerichte testomgeving.

Extra verificaties voor langdurig onderhoud

Als je de verantwoordelijkheden van de gedeelde Factory en elke PendingRequest scheidt, kan de gebruikende kant de gewone testmethoden van Laravel ongewijzigd gebruiken. Zorg eerst voor een implementatie die via de gedeelde Factory loopt en voor tests die dat controleren, in plaats van eerst een eigen fake-mechanisme te bouwen.

Geraadpleegde primaire bronnen

Laatst gewijzigd op 27 oktober 2026