-
Notifications
You must be signed in to change notification settings - Fork 147
PHP Tour Nantes 2012
Le support de cette conférence est également disponible au format pdf. Une vidéo de l'évolution du dépôt de code de atoum de sa création au jour de la conférence a servi d'introduction. Les scripts utilisés pour illustrer le propos sont également disponibles.
Suite à la démocratisation d'Internet en général et du Web en particulier, les services web sont aujourd'hui massivement utilisés par les programmes informatiques.
Ils sont en effet très pratiques puisqu'ils reposent sur le protocole HTTP et surtout parce qu'ils permettent au développeur de déléguer à des programmes tiers la réalisation de tâches complexes ou pour lesquelles il ne dispose pas des données nécessaires.
Il est ainsi par exemple possible d'utiliser un service web pour calculer un itinéraire routier et le tracer sur une carte géographique, pour interroger des bases de données publiques ou privées, pour effectuer des calculs complexes sur une infrastructure dédiée ou bien encore pour authentifier des utilisateurs, et cette liste est très loin d'être exhaustive.
Il n'est donc pas rare pour vous, développeur d'aujourd'hui, de devoir intégrer l'utilisation de un ou plusieurs services web dans vos développements.
Or, depuis quelque temps, afin d'améliorer la qualité de votre code et donc de vos livrables, vous avez décidé de mettre en place des tests sous la forme de un ou plusieurs programmes informatiques qui vérifient lors de leur exécution que votre code se comporte effectivement comme il le doit.
Vous exécutez donc ces tests très souvent, soit partiellement, soit complètement, afin de vous assurer que les dernières modifications que vous avez effectué sur votre programme n'ont pas eu d'impacts négatifs sur son comportement.
Et grâce à l'intégration continue, ces tests sont même exécutés automatiquement dans leur intégralité, sans la moindre intervention de votre part.
Vous écrivez donc des tests unitaires, parfois même avant de concevoir le code de votre programme, car vous faites du développement piloté par les tests, et afin d'avoir la meilleure couverture possible, vous complétez ces tests unitaires par des tests d'intégrations, eux-mêmes garantis par des tests fonctionnels et des tests manuels.
Ainsi, vous pouvez détecter et corriger au plus tôt la plupart sinon la totalité de vos erreurs et garantir la qualité de votre code et le respect des besoins fonctionnels de l'utilisateur final à votre client.
Or, l'utilisation de services web dans vos tests peut vous poser un certain nombre de problèmes susceptibles de vous donner extrêmement mal au crâne, voir même vous donner l'envie d'abandonner toute politique relative à la qualité logicielle.
Le premier problème et non l'un des moindres est que un ou plusieurs services web avec lesquels vous devez communiquer peuvent très bien ne pas être accessibles au moment de l'exécution de vos tests, et cela pour de multiples raisons.
Votre connexion à Internet peut en effet ne pas être opérationnelle parce que la femme de ménage a débranché par inadvertance un câble réseau, parce que le fournisseur du service rencontre des difficultés techniques ou vous a retiré par erreur vos autorisation d'accès, ou bien encore parce que votre administrateur réseau préféré vient de mettre en place un PALC, connu également sous le nom de « Proxy À la Con ».
Ou bien encore parce qu'un ouragan a rendu inaccessible une partie d'Internet et qu'en conséquence, votre fournisseur de service est obligé de ravitailler en carburant ses groupes électrogènes en trimbalant des seaux de carburants dans des escaliers sur plusieurs étages.
Mais en fait, peu importe la raison, puisque la conséquence est toujours la même, à savoir des tests qui ne passent pas. Vous allez alors devoir en chercher la raison, alors qu'à première vue, il n'y a strictement aucune raison pour que vous obteniez des échecs. Et dans la plupart des cas, vous ne pourrez rien faire pour corriger le problème, car s'il peut être plus ou moins facile pour vous d'être à nouveau connecté à Internet si l'origine du problème est un câble débranché, vous ne pourrez par contre pas faire grand chose pour faire disparaître les conséquences d'un ouragan ou contourner un PALC (du moins en restant dans la légalité).
Dans ce cas, vous aurez donc passé du temps à résoudre un problème dont vous n'êtes pas responsable et que vous n'aurez pas les moyens de résoudre. Les tests ne seront alors non plus avantage mais un handicap, car ils vous feront perdre du temps au lieu de vous en faire gagner.
Et comme ils sont exécutés très souvent et très régulièrement en fonction de l'avancement de vos développements, les tests peuvent au final vous faire perdre énormément de temps et donc d'argent, notamment si votre connexion à Internet n'est pas fiable ou si les services web que vous utilisez sont très souvent inaccessibles.
Dans le pire des cas, ceci peut vous amener à ne plus prendre le temps de chercher la cause des tests en échecs, car vous serez plus ou moins persuadé systématiquement que les coupables sont les services web. En conséquence, vos tests seront devenus inutiles et par ricochet, vous ne serez alors plus en mesure d'être certain que ce n'est pas votre code qui provoque ces échecs. Vous ne tarderez alors plus à arrêter de les développer et de les maintenir, avec tout les risques que cela implique en terme de qualité et les problèmes correspondants qui ne manqueront pas de survenir à plus ou moins court terme.
Un autre problème posé par l'utilisation de services web sont les politiques d'accès à ces derniers. Un service peut en effet par exemple ne vous autoriser qu'un certain nombre d'accès par jour, semaine, mois ou année, pour des raisons techniques et/ou commerciales, ou bien encore son utilisation peut être facturée en fonction de son utilisation.
Or, par nature, vos tests sont exécutés très souvent.
Il est donc très possible qu'ils consomment l'intégralité du quota d'accès qu'il vous a été alloué et que vous n'ayez alors plus la possibilité d'exploiter le service en production, notamment si au cours de vos développements vous avez généré par mégarde une ou plusieurs boucles infinies qui ont généré plusieurs milliers de requêtes en quelques secondes sur le service.
Dans le pire des cas, vous pourrez même vous retrouver sur une liste noire pour cause d'utilisation abusive du service concerné et donc dans l'impossibilité totale de l'utiliser pendant une période plus ou moins longue, en fonction du bon vouloir de son fournisseur.
Et pour la même raison, si l'accès au service implique un coût financier, vous pouvez vite vous retrouver avec une facture conséquente, alors même que vous n'avez même pas commencé à utiliser le service en production.
J'ajoute que même lorsqu'il n'est pas utilisé directement dans le cadre d'un test, un service web peut être un problème pour vos développement.
En effet, il peut s'écouler un certain temps avant que vous obteniez du fournisseur du service les informations nécessaires permettant de l'exploiter, à cause par exemple d'une négociation commerciale qui traîne en longueur ou bien parce que son utilisation nécessite l'obtention d'un certificat SSL qui permettra de vous authentifier et que pour l'obtenir, vous devez remplir un dossier de 500 pages qui sera traité dans les meilleurs délais, à savoir d'ici 15 à 300 jours, à la condition bien évidemment que vous ayez bien fourni tous les justificatifs demandés.
Or, en toute logique, vous ne pourrez commencer à travailler avec le service concerné et donc à l'intégrer dans vos tests que lorsque vous disposerez de ces informations, et si vous les obtenez trop tardivement, vous serez contraint de développer « à l'arrache » ou bien vous ne parviendrez pas à tenir vos délais de livraison, ce qui peut encore une fois avoir de sérieuses répercussions financières.
D'ailleurs, en parlant de délais, du fait de sa relative lenteur par rapport à du code exécuté localement, un services web est susceptible de ralentir plus ou moins significativement l'exécution de vos tests.
Les performances d'un service web dépendent en effet non seulement de celles de votre connexion au réseau qui l'héberge, mais également de celles de l'infrastructure technique de son fournisseur et de la qualité de son code.
Et si par hasard l'un des maillons de cette chaîne présente une faiblesse, vos tests vont s'exécuter plus ou moins lentement parce que le service n'aura pas été capable de vous répondre suffisamment rapidement.
Et si d'aventure vous utilisez un service très populaire ou qui est en train de devenir à la mode, vos tests peuvent tout simplement échouer car le fournisseur ne dispose pas des infrastructures nécessaires pour répondre correctement à l'ensemble des requêtes qu'il reçoit.
Je passerais rapidement sur les problèmes du même type posés par les attaques de type dénie de service que peuvent subir vos fournisseurs ou vous-même et qui rendront totalement inutilisables vos services web pendant une durée indéterminée.
Enfin, il est également possible que les réponses fournies par un service web évoluent au cours du temps parce que les données qu'il utilise ont été corrigées ou enrichies.
Or, vous ne vous en rendrez compte que lorsque vos tests seront en échec parce que les données qu'ils ont reçu de ces services ne sont plus celles que vous aviez prévue lors de leur rédaction.
Bref, utiliser un services web au sein d'un test revient à jouer à la roulette russe.
Souvent, il n'y a aucun problème, mais lorsqu'il y en a un, ses conséquences peuvent être très importantes.
Heureusement, il existe une solution permettant de résoudre toutes ces problèmatiques.
En effet, la totalité des problèmes que vous pouvez être amené à rencontrer en utilisant des services web dans vos test sont induits par le fait que vous n'avez absolument aucun contrôle sur ces derniers.
Pour les éviter ou les résoudre, il vous suffit donc d'obtenir ce contrôle, même si cela semble à première vue impossible.
En effet, par définition, un service web est indépendant de votre code et vous n'avez donc aucun moyen de définir son comportement.
Cependant, vos tests ont-ils réellement besoin d'un véritable service web pour valider le fonctionnement de votre code ?
Sont-ils réellement obligés de se connecter à un réseau, d'interroger un serveur et d'attendre sa réponse ?
La réponse est négative.
En effet, un test valide le comportement de votre code en fonction de la réponse que ce dernier est censé recevoir de la part du ou des services web.
La seule chose dont a réellement besoin votre code pour fonctionner correctement est donc une réponse de la part du service web et la façon dont cette réponse est obtenue importe peu.
L'établissement de la connexion au réseau hébergeant le service, l'interrogation de son serveur et l'attente de sa réponse sont un moyen et non une fin pour acquérir les données nécessaires à votre code.
La solution a l'ensemble des problèmes que vous êtes susceptible de rencontrer en tant que développeur lorsque vous testez du code utilisant un ou plusieurs services web est donc de simuler ces derniers.
Ainsi, vous pourrez au cours de vos tests fournir à votre code les données requises, sans être dépendant d'autre chose que de votre propre infrastructure technique et logiciel.
De plus, vous pourrez alors définir très précisément le comportement des « services » que vous utilisez en fonction des différents tests que vous souhaitez effectuer.
Vous pourrez donc simuler par exemple un refus d'authentification, une réponse incomplète ou dans un format différent de celui que vous aviez prévu, l'absence de réponse, bref, tout ce qui pourra vous permettre de valider le comportement de votre code lorsqu'un service ne se comporte pas comme il le devrait.
Et à contrario, vous pourrez tout aussi bien simuler une réponse correcte afin de vérifier que votre code se comporte comme il le doit lorsque tout se passe bien.
Pour cela, une première solution consiste à utiliser dans le cadre de vos tests un serveur de votre propre infrastructure dédié à la simulation des services web.
Vous pourrez alors en définir le comportement en fonction des besoins et il vous suffira d'y faire appel dans vos tests en lieu et place des véritables services web.
La seule contrainte induite par cette solution, outre le fait qu'il vous faut disposer du budget, du matériel et des compétences nécessaires à son installation et à sa configuration, est que vous devrez avoir la possibilité de définir via une directive ou un fichier de configuration la ou les adresses qui devront être utilisées pour accéder aux services web utilisés par votre code respectivement en environnement de test et en environnement de production.
Cependant, cette solution présente toujours des inconvénients, et plus particulièrement dans le cadre de tests unitaires.
En effet, elle nécessite toujours l'accès à un réseau qui peut ne pas être opérationnel ou accessible au moment de l'exécution de vos tests, par exemple parce que vous faites du développement piloté par les tests sur votre ordinateur portable dans le train qui vous emmène au PHP Tour et que vous ne disposez pas d'une clef 3G ou du VPN ad hoc pour vous connecter à votre infrastructure.
De plus, cela ne règle pas totalement le problème posé par la perte de performance de vos tests induite par la latence du réseau.
Or, un test unitaire peut être exécuté très souvent, parfois même plusieurs fois par minutes, surtout lorsque l'on fait du développement piloté par les tests.
La moindre perte de temps à ce niveau implique donc une perte de productivité, et à contrario, le moindre gain de temps entraîne une augmentation de productivité.
Certes, pour contourner le problème, vous pourriez installer et configurer le logiciel nécessaire sur votre poste de travail, mais cela suppose que vous disposiez à la fois des compétences, des droits d'administration nécessaires, de l'éventuelle licence adéquate et du temps nécessaire pour le faire.
Or, ce n'est pas forcément le cas et d'autant plus si le logiciel que vous utilisez en tant que proxy est payant.
La solution consiste donc à ne pas du tout utiliser le réseau lors des tests unitaires et à utiliser en remplacement des adapteurs ou des bouchons.
Je vais donc maintenant vous présenter deux cas pratiques d'utilisation de ces deux outils en m'appuyant sur atoum, le framework de test unitaire que je développe, car il en dispose en standard et permet de plus de les mettre en œuvre très rapidement et facilement.
Un adapteur est un objet qui joue le rôle de proxy entre une fonction native du langage et votre code.
En clair, lorsque vous utiliserez un adapteur dans votre code, lorsque ce dernier fera appel à une fonction native du langage, cet appel sera intercepté par l'adapteur.
Ce dernier décidera alors, en fonction de sa configuration, s'il doit effectivement exécuter la fonction cible et en retourner le résultat ou au contraire retourner une valeur arbitraire.
Cet outil est donc très utile dans le cas ou vous accéder à un service web à l'aide d'une fonction PHP telle que file_get_contents() ou encore à l'aide de l'extension CURL, puisque cette dernière ne dispose pas d'une interface objet.
Si l'adapteur permet de surcharger les fonctions natives de PHP, le mock permet quand à lui de simuler une instance d'une classe spécifique, que cette dernière soit spécifique à l'utilisateur ou bien créée par le développeur.
Tout comme l'adapteur, il permet de définir le comportement que doit la ou les méthodes appelées par l'instance.
Cet outil est donc très utile dans le cas ou vous accéder à un service web via une classe comme soapClient, par exemple.
L'adapteur et les mocks sont donc des outils puissants qui permettent au développeur de s'abstraire des services web dans le cadre de ses tests.
Pour autant, ils ne sont pas une solution miracle à tout les problèmes, et en conséquence, les tests unitaires qui les utilisent devraient toujours être complétés par des tests d'intégration, des tests fonctionnels ainsi que par des tests manuels, afin d'avoir une couverture du code la plus exhaustive possible.
En effet, les différents types de test sont complémentaires et se recouvrent mutuellement.
En conséquence, c'est une erreur de croire que se contenter uniquement de tests unitaires ou de tests fonctionnels permet de garantir que tous les comportements possibles du code ont été testés et qu'il ne reste pas des erreurs.
De plus, même si atoum est conçu pour simplifier au maximum leur mise en œuvre et la maintenance des tests correspondants, l'utilisation de ces outils apporte une couche de complexité supplémentaire et il faut donc les utiliser à bon escient, en fonction de leur valeur ajoutée.
Ainsi, il ne sera peut être pas forcément pertinent et donc rentable d'investir le temps nécessaire à la mise en place d'adapteur et de mocks dans vos tests si vos services web sont suffisamment fiables et performants à vos yeux.