Passer au contenu principal
Lorsqu’un package doit consommer une API externe, il faut concilier deux exigences : hériter de la configuration du client HTTP gérée par Laravel et ne pas conserver la configuration d’un appel à l’autre. Créer sa propre Factory ou conserver longtemps une requête non envoyée empêche les fakes de l’application consommatrice de s’appliquer. Cette page a été vérifiée sur l’implémentation de Laravel Framework v13.39.0. Pour les opérations de base, consultez Client HTTP, et pour préparer l’environnement de test, consultez Tester des packages.

Partager la Factory, créer une PendingRequest à chaque fois

Le client HTTP de Laravel repose sur des objets aux responsabilités distinctes. FoundationServiceProvider enregistre la Factory en singleton, et la façade Http est résolue depuis le conteneur avec le même nom de classe. Ainsi, dans une application Laravel classique, les échanges effectués via la Factory reçue du conteneur peuvent aussi être vérifiés avec Http::fake(). Il est possible d’utiliser directement la façade, mais cette page injecte la Factory afin de rendre les dépendances explicites. Les méthodes habituelles de configuration de requête appelées sur la Factory, comme baseUrl(), sont transférées par __call() vers une nouvelle PendingRequest. createPendingRequest() transmet les callbacks de fake et la restriction des stray requests en vigueur au moment de la création, et newPendingRequest() applique les middlewares et options communs.
Si le package instancie new Factory() ou le client d’une autre bibliothèque HTTP, les fakes de la Factory partagée et Http::preventStrayRequests() ne sont pas hérités. Ces fonctionnalités ne bloquent pas toutes les communications du processus. Pour un client destiné à Laravel, utilisez la Factory partagée, et si vous fournissez un client indépendant, prévoyez une méthode de test dédiée à ce client.

Regrouper les opérations d’API dans un petit client

Prenons l’exemple d’un package Acme\Catalog qui récupère des produits depuis une API de catalogue fictive. src/CatalogClient.php ne conserve que la Factory et l’URL de base, et la requête est créée pendant l’exécution de product().
Avec cette conception, si vous appelez product(42) après product(42, 'ja') sur le même client, l’en-tête Accept-Language ajouté lors de l’appel précédent ne subsiste pas. La configuration commune définie sur la Factory par l’application consommatrice continue de s’appliquer à chaque requête.
  • Appliquez la configuration propre au package sur la PendingRequest nouvellement créée. Évitez de définir Http::globalOptions() lors de l’initialisation du package, car cela affecterait aussi des échanges sans rapport.
  • N’envoyez rien dans le constructeur. Concevez le package pour que la simple résolution du service, le démarrage d’Artisan ou la publication du fichier de configuration ne se connectent pas à l’API externe.
  • throw() convertit les 4xx et 5xx en RequestException. Distinguez-les des ConnectionException dues à un échec de connexion ou à un timeout, et ne remplacez pas un échec par une valeur de succès comme un tableau vide.
  • json(flags: JSON_THROW_ON_ERROR) convertit un JSON invalide en JsonException. L’exemple vérifie seulement qu’il s’agit d’un tableau ; avec une vraie API, validez aussi les champs obligatoires et les types de données.
S’il s’agit seulement de mutualiser l’URL de base ou les en-têtes, les macros HTTP de la documentation officielle sont aussi une option. Si le package fournit les opérations d’API, l’inspection des valeurs de retour et le contrat d’exceptions, le séparer dans une classe dédiée comme celle-ci facilite la maintenance. Pour le fonctionnement des macros, consultez Le trait Macroable.

Enregistrer le client dans un service provider

Préparez la configuration initiale dans config/catalog.php du package. N’y stockez ni client HTTP ni closure ; ne mettez que des valeurs pouvant être placées dans le cache de configuration.
src/CatalogServiceProvider.php fusionne la configuration et transmet la Factory partagée lors de la résolution du client. Il ne remplace pas le binding de la Factory elle-même.
Le provider est chargé par exemple via la découverte automatique des packages. L’utilisateur publie la configuration et remplace l’URL de base dans config/acme-catalog.php par celle de la vraie API. Le catalog.example.test de l’exemple est un hôte fictif destiné aux explications et aux fakes.
Côté application, le client est résolu par injection dans le constructeur ou via le conteneur. La requête de récupération du produit n’est envoyée qu’au moment de cet appel.
L’enregistrement utilise bind(), mais si l’objet qui conserve le client est un singleton, cette référence n’est pas recréée à chaque fois. L’essentiel est de ne pas stocker dans des propriétés la langue, les identifiants d’authentification ou l’objet de requête propres à un appel. Pour plus de détails, consultez Concevoir le cycle de vie des services d’un package. La surcharge de la configuration et la mise à jour du cache lors du déploiement sont traitées dans Fusion et cache de la configuration d’un package. Gérez l’URL de base comme une configuration d’application de confiance, et ne laissez pas les entrées de requête désigner librement la destination.

Vérifier le vrai client avec des fakes

Si vous mockez le client lui-même, vous ne pouvez pas vérifier qu’il utilise la Factory partagée ni qu’il construit correctement l’URL et les en-têtes. Dans les tests du package, résolvez le vrai CatalogClient depuis le conteneur et ne simulez que les réponses HTTP. Voici un exemple exécuté dans l’environnement de test Orchestra Testbench. preventStrayRequests() est activé dans le setUp() de chaque test. Dans les tests qui communiquent, définissez des réponses uniquement pour les URL attendues et n’ajoutez pas de repli '*', afin de détecter aussi les erreurs d’URL.
Dans le deuxième test, le fake est défini après la résolution du client. Comme le client conserve la Factory et crée la PendingRequest au moment de l’appel réel, le fake est pris en compte même dans cet ordre. Si la conception est modifiée pour créer et conserver la PendingRequest dans le constructeur, ce test détectera que les fakes ajoutés après la création ne sont plus appliqués. assertSent() vérifie qu’au moins une requête correspondante existe. Combinez-le avec assertSentCount() pour ne pas laisser passer des envois supplémentaires non souhaités. assertSentInOrder() vérifie le nombre et l’ordre des requêtes, ce qui permet aussi de contrôler l’isolation de l’état lorsque le même client est utilisé plusieurs fois de suite. La protection est configurée après parent::setUp(). Ce test seul ne permet pas de vérifier les échanges effectués pendant le démarrage du provider ; conservez donc une conception qui ne communique pas lors de l’enregistrement et du démarrage.
Les fakes permettent de vérifier les requêtes construites par le client et le traitement des réponses préparées. Ils ne garantissent ni l’authentification du vrai service, ni la connexion TLS, ni les durées réelles de timeout, ni les changements de spécification de l’API externe. Les vérifications impliquant des connexions externes doivent être menées séparément, dans un environnement de test explicitement préparé.

Vérifications à ajouter pour la maintenance à long terme

En séparant les responsabilités de la Factory partagée et de chaque PendingRequest, l’application consommatrice peut utiliser telles quelles les méthodes de test habituelles de Laravel. Plutôt que de commencer par créer votre propre mécanisme de fake, préparez d’abord une implémentation qui passe par la Factory partagée et les tests qui le vérifient.

Sources primaires consultées

Dernière modification le 27 octobre 2026