J'ai quelques problèmes en essayant d'encapsuler mon code pour être utilisé dans les tests unitaires. Le problème est le suivant. J'ai l'interface IHttpHandler:
public interface IHttpHandler
{
HttpClient client { get; }
}
Et la classe qui l'utilise, HttpHandler:
public class HttpHandler : IHttpHandler
{
public HttpClient client
{
get
{
return new HttpClient();
}
}
}
Et puis la classe Connection, qui utilise simpleIOC pour injecter l'implémentation client:
public class Connection
{
private IHttpHandler _httpClient;
public Connection(IHttpHandler httpClient)
{
_httpClient = httpClient;
}
}
Et puis j'ai un projet de test unitaire qui a cette classe:
private IHttpHandler _httpClient;
[TestMethod]
public void TestMockConnection()
{
var client = new Connection(_httpClient);
client.doSomething();
// Here I want to somehow create a mock instance of the http client
// Instead of the real one. How Should I approach this?
}
Maintenant, évidemment, j'aurai des méthodes dans la classe Connection qui récupéreront des données (JSON) depuis mon back-end. Cependant, je veux écrire des tests unitaires pour cette classe, et évidemment je ne veux pas écrire de tests contre le vrai back-end, plutôt un simulé. J'ai essayé de google une bonne réponse à cela sans grand succès. Je peux et ai utilisé Moq pour me moquer avant, mais jamais sur quelque chose comme httpClient. Comment dois-je aborder ce problème?
Merci d'avance.
la source
HttpClient
dans votre interface est là où se situe le problème. Vous obligez votre client à utiliser laHttpClient
classe concrète. Au lieu de cela, vous devez exposer une abstraction duHttpClient
.Réponses:
Votre interface expose la
HttpClient
classe concrète , donc toutes les classes qui utilisent cette interface y sont liées, cela signifie qu'elle ne peut pas être moquée.HttpClient
n'hérite d'aucune interface, vous devrez donc écrire la vôtre. Je suggère un motif à la décoration:Et votre classe ressemblera à ceci:
Le point dans tout cela est que cela
HttpClientHandler
crée le sienHttpClient
, vous pouvez bien sûr créer plusieurs classes qui implémententIHttpHandler
de différentes manières.Le principal problème avec cette approche est que vous écrivez effectivement une classe qui appelle simplement des méthodes dans une autre classe, mais vous pouvez créer une classe qui hérite de
HttpClient
(voir l'exemple de Nkosi , c'est une bien meilleure approche que la mienne). La vie serait beaucoup plus facile si vousHttpClient
aviez une interface dont vous pourriez vous moquer, malheureusement ce n'est pas le cas.Cet exemple n'est cependant pas le ticket d'or.
IHttpHandler
repose toujours surHttpResponseMessage
, qui appartient à l'System.Net.Http
espace de noms, donc si vous avez besoin d'autres implémentations queHttpClient
, vous devrez effectuer une sorte de mappage pour convertir leurs réponses enHttpResponseMessage
objets. Ce n'est bien sûr un problème que si vous avez besoin d'utiliser plusieurs implémentations deIHttpHandler
mais il ne semble pas que vous le fassiez, donc ce n'est pas la fin du monde, mais c'est quelque chose à penser.Quoi qu'il en soit, vous pouvez simplement vous moquer
IHttpHandler
sans avoir à vous soucier de laHttpClient
classe concrète telle qu'elle a été abstraite.Je recommande de tester les méthodes non asynchrones , car elles appellent toujours les méthodes asynchrones mais sans avoir à vous soucier des méthodes asynchrones de test unitaire, voir ici
la source
L'extensibilité de HttpClient réside dans le
HttpMessageHandler
passé au constructeur. Son objectif est d'autoriser des implémentations spécifiques à la plate-forme, mais vous pouvez également vous en moquer. Il n'est pas nécessaire de créer un wrapper décorateur pour HttpClient.Si vous préférez un DSL à l'utilisation de Moq, j'ai une bibliothèque sur GitHub / Nuget qui facilite un peu les choses: https://github.com/richardszalay/mockhttp
la source
var client = new HttpClient()
avecvar client = ClientFactory()
et configurer un champinternal static Func<HttpClient> ClientFactory = () => new HttpClient();
et au niveau de test , vous pouvez réécrire ce domaine.HttpClientFactory
comme dansFunc<HttpClient>
. Étant donné que je considère HttpClient comme un simple détail d'implémentation et non comme une dépendance, j'utiliserai la statique comme je l'ai illustré ci-dessus. Je suis tout à fait d'accord avec les tests manipulant les internes. Si je me soucie du pur-isme, je vais mettre en place des serveurs complets et tester les chemins de code en direct. L'utilisation de tout type de simulation signifie que vous acceptez l'approximation du comportement et non le comportement réel.Je suis d'accord avec certaines des autres réponses selon lesquelles la meilleure approche consiste à se moquer de HttpMessageHandler plutôt que d'envelopper HttpClient. Cette réponse est unique en ce qu'elle injecte toujours HttpClient, ce qui lui permet d'être un singleton ou géré avec une injection de dépendance.
"HttpClient est destiné à être instancié une fois et réutilisé tout au long de la vie d'une application." ( Source ).
Mocking HttpMessageHandler peut être un peu délicat car SendAsync est protégé. Voici un exemple complet, utilisant xunit et Moq.
la source
mockMessageHandler.Protected()
était le tueur. Merci pour cet exemple. Cela permet d'écrire le test sans modifier du tout la source..ReturnsAsync(new HttpResponseMessage {StatusCode = HttpStatusCode.OK, Content = new StringContent(testContent)})
C'est une question courante, et j'étais fortement du côté voulant la possibilité de se moquer de HttpClient, mais je pense que j'ai finalement réalisé que vous ne devriez pas vous moquer de HttpClient. Cela semble logique de le faire, mais je pense que nous avons subi un lavage de cerveau par des choses que nous voyons dans les bibliothèques open source.
Nous voyons souvent des «clients» que nous nous moquons dans notre code afin de pouvoir tester de manière isolée, nous essayons donc automatiquement d'appliquer le même principe à HttpClient. HttpClient fait beaucoup; vous pouvez le considérer comme un gestionnaire pour HttpMessageHandler, donc vous ne voulez pas vous moquer de cela, et c'est pourquoi il n'a toujours pas d'interface. La partie qui vous intéresse vraiment pour les tests unitaires, ou même la conception de vos services, est le HttpMessageHandler, car c'est ce qui renvoie la réponse, et vous pouvez vous en moquer.
Il convient également de souligner que vous devriez probablement commencer à traiter HttpClient comme une affaire plus importante. Par exemple: réduisez au minimum votre installation de nouveaux HttpClients. Réutilisez-les, ils sont conçus pour être réutilisés et utiliser une tonne de ressources en moins si vous le faites. Si vous commencez à le traiter comme une affaire plus importante, vous vous sentirez beaucoup plus mal de vouloir vous en moquer et maintenant le gestionnaire de messages commencera à être la chose que vous injectez, pas le client.
En d'autres termes, concevez vos dépendances autour du gestionnaire plutôt que du client. Mieux encore, des "services" abstraits qui utilisent HttpClient qui vous permettent d'injecter un gestionnaire et de l'utiliser comme dépendance injectable à la place. Ensuite, dans vos tests, vous pouvez simuler le gestionnaire pour contrôler la réponse pour la configuration de vos tests.
Wrapping HttpClient est une perte de temps insensée.
Mise à jour: voir l'exemple de Joshua Dooms. C'est exactement ce que je recommande.
la source
Comme il est mentionné également dans les commentaires que vous devez abstraite loin la
HttpClient
façon de ne pas être couplé à elle. J'ai fait quelque chose de similaire dans le passé. J'essaierai d'adapter ce que j'ai fait à ce que vous essayez de faire.Examinez d'abord la
HttpClient
classe et décidez des fonctionnalités qu'elle fournissait qui seraient nécessaires.Voici une possibilité:
Encore une fois, comme indiqué précédemment, c'était à des fins particulières. J'ai complètement fait abstraction de la plupart des dépendances à tout ce qui concerne
HttpClient
et me suis concentré sur ce que je voulais retourner. Vous devez évaluer la façon dont vous souhaitez abstraire leHttpClient
pour fournir uniquement les fonctionnalités nécessaires que vous souhaitez.Cela vous permettra désormais de vous moquer uniquement de ce qui doit être testé.
Je recommanderais même de supprimer
IHttpHandler
complètement et d'utiliser l'HttpClient
abstractionIHttpClient
. Mais je ne choisis tout simplement pas car vous pouvez remplacer le corps de votre interface de gestionnaire par les membres du client abstrait.Une implémentation de
IHttpClient
peut ensuite être utilisée pour envelopper / adapter un objet réel / concretHttpClient
ou tout autre objet, qui peut être utilisé pour faire des requêtes HTTP, car ce que vous vouliez vraiment était un service qui fournissait cette fonctionnalité comme étantHttpClient
spécifiquement associé . L'utilisation de l'abstraction est une approche propre (à mon avis) et SOLIDE et peut rendre votre code plus maintenable si vous avez besoin de changer le client sous-jacent pour autre chose à mesure que le cadre change.Voici un extrait de la façon dont une mise en œuvre pourrait être effectuée.
Comme vous pouvez le voir dans l'exemple ci-dessus, une grande partie du travail lourd généralement associé à l'utilisation
HttpClient
est cachée derrière l'abstraction.Votre classe de connexion peut ensuite être injectée avec le client abstrait
Votre test peut alors simuler ce qui est nécessaire pour votre SUT
la source
HttpClient
- dessus et un adaptateur pour créer votre instance spécifique à l'aide deHttpClientFactory
. Cela rend le test de la logique au-delà de la requête HTTP trivial, ce qui est le but ici.En me basant sur les autres réponses, je suggère ce code, qui n'a pas de dépendances extérieures:
la source
HttpMessageHandler
vous-même rend cela presque impossible - et vous devez le faire car les méthodes le sontprotected internal
.Je pense que le problème est que vous l'avez juste un peu à l'envers.
Si vous regardez la classe ci-dessus, je pense que c'est ce que vous voulez. Microsoft recommande de garder le client en vie pour des performances optimales, ce type de structure vous permet donc de le faire. Le HttpMessageHandler est également une classe abstraite et donc mockable. Votre méthode de test ressemblerait alors à ceci:
Cela vous permet de tester votre logique tout en se moquant du comportement du HttpClient.
Désolé les gars, après avoir écrit ceci et essayé moi-même, j'ai réalisé que vous ne pouvez pas vous moquer des méthodes protégées sur HttpMessageHandler. J'ai ensuite ajouté le code suivant pour permettre l'injection d'une maquette appropriée.
Les tests écrits avec ceci ressemblent alors à ceci:
la source
Un de mes collègues a remarqué que la plupart des
HttpClient
méthodes appellent toutesSendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
sous le capot, qui est une méthode virtuelle hors deHttpMessageInvoker
:Donc, de loin, le moyen le plus simple de se moquer
HttpClient
était de simplement se moquer de cette méthode particulière:et votre code peut appeler la plupart (mais pas la totalité) des
HttpClient
méthodes de classe, y compris unVérifiez ici pour confirmer https://github.com/dotnet/corefx/blob/master/src/System.Net.Http/src/System/Net/Http/HttpClient.cs
la source
SendAsync(HttpRequestMessage)
directement. Si vous pouvez modifier votre code pour ne pas utiliser cette fonction pratique, se moquer directement de HttpClient en remplaçantSendAsync
est en fait la solution la plus propre que j'ai trouvée.Une alternative serait de configurer un serveur HTTP stub qui renvoie des réponses prédéfinies en fonction du modèle correspondant à l'URL de la demande, ce qui signifie que vous testez de vraies requêtes HTTP et non des simulations. Historiquement, cela aurait demandé des efforts de développement importants et aurait été bien trop lent à être pris en compte pour les tests unitaires, mais la bibliothèque OSS WireMock.net est facile à utiliser et suffisamment rapide pour être exécutée avec de nombreux tests. La configuration consiste en quelques lignes de code:
Vous pouvez trouver plus de détails et des conseils sur l'utilisation de wiremock dans les tests ici.
la source
Voici une solution simple, qui a bien fonctionné pour moi.
Utilisation de la bibliothèque moqueuse Moq.
Source: https://gingter.org/2018/07/26/how-to-mock-httpclient-in-your-net-c-unit-tests/
la source
SendAsync
toute façon, donc aucune configuration supplémentaire n'est requise.Je ne suis pas convaincu par la plupart des réponses.
Tout d'abord, imaginez que vous souhaitez tester unitaire une méthode qui utilise
HttpClient
. Vous ne devez pas instancierHttpClient
directement dans votre implémentation. Vous devez injecter une usine avec la responsabilité de vous fournir une instance deHttpClient
. De cette façon, vous pouvez vous moquer plus tard de cette usine et retourner celle queHttpClient
vous voulez (par exemple: une maquetteHttpClient
et pas la vraie).Donc, vous auriez une usine comme celle-ci:
Et une mise en œuvre:
Bien sûr, vous devrez enregistrer cette implémentation dans votre conteneur IoC. Si vous utilisez Autofac, ce serait quelque chose comme:
Maintenant, vous auriez une implémentation correcte et testable. Imaginez que votre méthode ressemble à quelque chose comme:
Maintenant, la partie test.
HttpClient
s'étendHttpMessageHandler
, ce qui est abstrait. Créons un "simulacre" deHttpMessageHandler
qui accepte un délégué de sorte que lorsque nous utilisons le simulacre, nous pouvons également configurer chaque comportement pour chaque test.Et maintenant, et avec l'aide de Moq (et FluentAssertions, une bibliothèque qui rend les tests unitaires plus lisibles), nous avons tout ce qu'il faut pour tester unitaire notre méthode PostAsync qui utilise
HttpClient
De toute évidence, ce test est idiot, et nous testons vraiment notre simulation. Mais vous voyez l'idée. Vous devez tester une logique significative en fonction de votre implémentation, telle que ..
Le but de cette réponse était de tester quelque chose qui utilise HttpClient et c'est une belle façon propre de le faire.
la source
Rejoindre la fête un peu tard, mais j'aime utiliser wiremocking ( https://github.com/WireMock-Net/WireMock.Net ) chaque fois que possible dans le test d'intégration d'un microservice de base dotnet avec des dépendances REST en aval.
En implémentant un TestHttpClientFactory étendant le IHttpClientFactory, nous pouvons remplacer la méthode
HttpClient CreateClient (nom de chaîne)
Ainsi, lorsque vous utilisez les clients nommés dans votre application, vous contrôlez le retour d'un HttpClient câblé à votre wiremock.
La bonne chose à propos de cette approche est que vous ne changez rien dans l'application que vous testez, et permet aux tests d'intégration de cours de faire une requête REST réelle à votre service et de se moquer du json (ou autre) que la requête en aval réelle devrait renvoyer. Cela conduit à des tests concis et aussi peu de moquerie que possible dans votre application.
et
la source
Depuis
HttpClient
utiliser laSendAsync
méthode pour tout exécuterHTTP Requests
, vous pouvez utiliser une méthode et vousoverride SendAsync
moquer du fichierHttpClient
.Pour cette enveloppe créant
HttpClient
uninterface
, quelque chose comme ci-dessousEnsuite, utilisez ci-dessus
interface
pour l'injection de dépendances dans votre service, exemple ci-dessousMaintenant, dans le projet de test unitaire, créez une classe d'assistance pour les moqueries
SendAsync
. Ici, c'est uneFakeHttpResponseHandler
classe quiinheriting
DelegatingHandler
fournira une option pour remplacer laSendAsync
méthode. Après avoir remplacé laSendAsync
méthode, vous devez configurer une réponse pour chaque méthodeHTTP Request
appelanteSendAsync
, pour cela, créez unDictionary
avec aukey
furUri
etvalue
àHttpResponseMessage
mesure que chaque fois qu'il y a unHTTP Request
et si lesUri
correspondancesSendAsync
renverront le fichier configuréHttpResponseMessage
.Créez une nouvelle implémentation pour
IServiceHelper
en se moquant du framework ou comme ci-dessous. CetteFakeServiceHelper
classe, nous pouvons utiliser pour injecter laFakeHttpResponseHandler
classe afin que chaque fois que leHttpClient
créé par cela,class
il l'utilise à laFakeHttpResponseHandler class
place de l'implémentation réelle.Et en test, configurez
FakeHttpResponseHandler class
en ajoutant leUri
et attenduHttpResponseMessage
. LeUri
doit être leservice
point de terminaison réel deUri
sorte que lorsque laoverridden SendAsync
méthode est appelée à partir de l'service
implémentation réelle, elle correspond auUri
inDictionary
et répond avec le configuréHttpResponseMessage
. Après avoir configuré, injectez leFakeHttpResponseHandler object
dans la fausseIServiceHelper
implémentation. Puis injectez leFakeServiceHelper class
dans le service réel qui permettra au service réel d'utiliser laoverride SendAsync
méthode.Lien GitHub: qui a un exemple d'implémentation
la source
Vous pouvez utiliser la bibliothèque RichardSzalay MockHttp qui se moque de HttpMessageHandler et peut renvoyer un objet HttpClient à utiliser pendant les tests.
GitHub MockHttp
PM> Package d'installation RichardSzalay.MockHttp
À partir de la documentation GitHub
Exemple de GitHub
la source
C'est une vieille question, mais je ressens le besoin d'étendre les réponses avec une solution que je n'ai pas vue ici.
Vous pouvez simuler l'assemly Microsoft (System.Net.Http), puis utiliser ShinsContext pendant le test.
Dépend de votre implémentation et de votre test, je suggérerais de mettre en œuvre toutes les actions souhaitées lorsque vous appelez une méthode sur HttpClient et que vous souhaitez simuler la valeur retournée. L'utilisation de ShimHttpClient.AllInstances simulera votre implémentation dans toutes les instances créées lors de votre test. Par exemple, si vous souhaitez simuler la méthode GetAsync (), procédez comme suit:
la source
J'ai fait quelque chose de très simple, car j'étais dans un environnement DI.
et le simulacre est:
la source
Tout ce dont vous avez besoin est une version de test de la
HttpMessageHandler
classe que vous passez àHttpClient
ctor. Le point principal est que votreHttpMessageHandler
classe de test aura unHttpRequestHandler
délégué que les appelants peuvent définir et gérer simplementHttpRequest
comme ils le souhaitent.Vous pouvez utiliser une instance de cette classe pour créer une instance HttpClient concrète. Via le délégué HttpRequestHandler, vous avez un contrôle total sur les requêtes http sortantes de HttpClient.
la source
Inspiré de la réponse de PointZeroTwo , voici un exemple utilisant NUnit et FakeItEasy .
SystemUnderTest
dans cet exemple se trouve la classe que vous voulez tester - aucun exemple de contenu n'est donné mais je suppose que vous l'avez déjà!la source
Il y aurait peut-être du code à changer dans votre projet actuel, mais pour les nouveaux projets, vous devriez absolument envisager d'utiliser Flurl.
https://flurl.dev
Il s'agit d'une bibliothèque cliente HTTP pour .NET avec une interface fluide qui permet spécifiquement la testabilité du code qui l'utilise pour effectuer des requêtes HTTP.
Il existe de nombreux exemples de code sur le site Web, mais en un mot, vous l'utilisez comme ceci dans votre code.
Ajoutez les utilisations.
Envoyez une requête get et lisez la réponse.
Dans les tests unitaires, Flurl agit comme un simulacre qui peut être configuré pour se comporter comme vous le souhaitez et également pour vérifier les appels qui ont été effectués.
la source
Après une recherche minutieuse, j'ai trouvé la meilleure approche pour y parvenir.
la source
Pour ajouter mes 2 cents. Pour simuler des méthodes de requête http spécifiques, soit Get ou Post. Cela a fonctionné pour moi.
la source