Transcription de l'épisode 19: Python unittest avec Robert Collins

By | avril 18, 2019

python pour débutant

Ceci est la transcription de Test & Code, épisode 19: Python unittest avec Robert Collins.

[music] Bienvenue dans Test and Code, un podcast sur le développement et les tests de logiciels. Épisode 19. Dans cet épisode, j'interviewe Robert Collins, le principal responsable de la maintenance du module unittest de Python. J'ai ré-étudié unittest récemment, et je voulais surtout poser à Robert une série de questions de clarification. Ceci est une discussion intermédiaire à avancée de unittest. Dans cette présentation, de nombreuses fonctionnalités d’unitest passent rapidement. Merci de me laisser savoir s’il ya quelque chose que vous voudriez que je décrive plus en profondeur, comme un article de blog ou un épisode futur. Quelques-uns des sujets que nous abordons sont les suivants: comment Robert est-il devenu le responsable de Unittest, unittest 2 en tant que backport évolutif, le paramétrage de tâches avec des tests de sous-tests et de scénarios de test, dont l'extension ressemble le plus étroitement aux appareils Pytest, en comparant Pytest et les tests unitest, unittest obtiendra toujours la réécriture d’affirmation et les modifications futures en unittest. Et nous couvrons plus de choses. Un petit mot sur la qualité audio. Il y a beaucoup de bonnes informations dans cette interview. Il y a aussi un bruit de hochet étrange qui en fait partie. J’ai fait de mon mieux pour le réduire en post-traitement, mais honnêtement, je ne suis pas un gourou de l’édition audio et de la suppression des sons indésirables. Je pense que les informations valent la peine de supporter cette gêne, mais je tiens à m'excuser quand même. Si vous savez comment régler ce problème, contactez-moi. Un merci spécial à mes merveilleux partisans de Patreon. Rendez-vous sur patreon.com/testpodcast et vous pourriez en devenir un aussi. Si vous êtes déjà un supporter, merci. Si vous ne soutenez pas, considérez-le. Nous couvrons beaucoup de sujets dans l’émission, et j’inclus des liens dans les notes d’émission à pythontesting.net/19. [music]

Brian: Salut Robert!
Robert: Hey Brian, comment vas-tu?
Brian: Je vais bien. J'apprécie vraiment votre venue. Souhaitez-vous vous présenter à mes auditeurs et me parler un peu de votre histoire et de votre parcours?
Robert: Donc, je m'appelle Robert Collins et je suis un défenseur motivé par les tests et une sorte de défenseur des tests dans un sens plus général depuis environ seize ans maintenant et, je ne sais pas, je finis par travailler sur l'outillage partout où je vais. liés aux tests ou aux tests associés, dans le cadre du bon fonctionnement des projets logiciels. Cela semble tellement fondamental. Ces jours-ci, c'est fantastique, parce que presque tout le monde a une base, disons, nous allons faire des tests et des tests unitaires, mais je me souviens du mauvais temps où des projets entiers utilisaient des projets Internet critiques. infrastructure qui n’avait pas de suite de tests. Ou qui en avait un qui était cassé, ce qui est presque pire.
Brian: Alors, sur quels types de projets travaillez-vous normalement? Sont-ils liés à Internet ou?
Robert: Vous savez, je ne peux pas penser à un projet qui n’a pas été relié à Internet d’une manière ou d’une autre, dans le dernier cas, vous savez, aussi longtemps que vous le souhaitez. Dernièrement, j’ai travaillé chez HP sur une pile ouverte, ce qui impliquait la pile ouverte elle-même, ainsi que le travail en amont de la pile ouverte dans l’écosystème sur lequel repose le développement de la pile ouverte. Ainsi, l’écosystème Python, ainsi que les bibliothèques et outils de test associés, ainsi que l’écosystème d’emballage, alors pip, qui est évidemment utilisé par un très grand nombre de personnes, et c’est amusant d’avoir changé.
Brian: Oh, alors vous avez aussi contribué au pip?
Robert: Oui, donc mes quinze minutes de gloire sur Internet ont été lorsque j'ai ajouté le cache de roues à pip. Donc, si vous téléchargez say numpy ou quelque chose d’autre, et le compilez pour l’installer sur un serveur virtuel, puis vous créez un nouveau serveur virtuel cinq minutes plus tard, si vous utilisez le pip 6, il le compilera à chaque fois et vous lancerez Python est si lent! Cela prend cinq minutes pour installer ce que j’utilise! ». Et si vous utilisez le pip 7 avec le cache de roue, ou peut-être que c'est 7.1, j'oublie le nombre exact, puis lorsque vous le faites la première fois, il le compile et construit une roue binaire, et en conserve une copie sur votre disque local, et lors de la prochaine virtualisation que vous réalisez, c’est une opération en une seconde pour décompresser la roue sur disque et, tout d’un coup, la sensation est agréable, rapide et vive.
Brian: Est-ce que ça marche même si je suis comme un serveur de pip local.
Robert: Oui, parce que c’est un cache côté client. Donc, tant que vous utilisez le même code utilisateur, parce que le cache est dans ~ / .cache, tant que vous utilisez le même code utilisateur et que vous ne le nettoyez pas à chaque fois ou quelque chose comme ça, alors oui, absolument.
Brian: d'accord. C'est super.
Brian: C’est sur ma liste de choses à faire de mettre en place un serveur pip pour notre groupe de travail.
Robert: Oui, quelque chose comme devpi peut être vraiment très utile. Et vous pouvez télécharger des roues binaires dans devpi. Il y a la nouvelle chose, la spécification mini linux, qui a été faite, et qui avec le pip 8.1 je pense que ça va, ça honorera ça, et ça vous laissera des roues monter sur PyPI, donc ce binaire le cache de roue devient un peu moins important. Donc, si vous avez des éléments qui ne peuvent pas être mis en cache pour une raison quelconque, ou si vous avez beaucoup de linux différents, alors construire une roue de cette spécification et l’envoyer pourrait être une chose avantageuse à faire.
Brian: d'accord. Maintenant, mes roues tournent dans ma tête. [laughs] Je ne voulais même pas dire que c'était un jeu de mots. Mais, ce que je voulais vraiment vous faire parler, c’était les choses les plus faibles et les plus connexes [05.39] Alors, comment en êtes-vous arrivé à faire partie des développeurs les plus modestes, des développeurs principaux ou un développeur, je ne suis pas sûr.
Robert: Ouais, je veux dire, unittest en Python a une assez longue histoire. Au fil des ans, il a été entretenu par un groupe de mainteneurs différents qui se sont montrés très attentifs et qui ont dit: regardez, je vais m'en occuper un moment. Michael Ford, qui en était le responsable actif à l'époque, m'intrigue. Nous avons collaboré avec lui sur des correctifs ou des idées. Je maintenais depuis longtemps des choses en dehors du noyau Python, comme des outils de test et des sous-unités, des fixtures, etc. Et nous avions un plan avec des tests, qui n’a pas encore abouti, mais nous avons pris des mesures. Le plan consistait à créer des prototypes d’améliorations, puis à les soumettre sous forme de correctifs, afin de les améliorer, et, comme je l’ai dit plus haut, certaines des choses que nous avons accomplies, comme le protocole de test de charge, ont été réalisées à partir d’outils de test. . Donc, je ne sais pas si je dois aller en profondeur ou en profondeur, je vais aller en profondeur, voir où cela nous mène.
Robert: Dans la suite de tests, il y a en mémoire un tas d’objets de cas de tests paramétrés avec une fonction qui va être exécutée et dont la classe est entourée, ce qui vous permet de vous accrocher aux méthodes d’installation, de démontage et d’aide. Et ils sont disposés dans un conteneur, une suite de tests. Et le conteneur peut avoir lui-même certains comportements. La configuration de la classe et celle du module sont donc implémentées en tant que comportements sur l'objet de la suite et non sur le scénario de test individuel.
Robert: Et c’est parce que, si vous avez trente-cinq objets de scénario de test distincts, comment savent-ils collaborer, créer des classes une fois au début et une fois à la fin? Et la réponse est que la suite, qui possède tous ces tests, examine la classe du prochain test qu’elle va exécuter pour décider s’il est temps de lancer le démontage pour le démontage de la classe. Et de même, décider de lancer ou non le démontage du module. Mais ceci est placé dans le noyau, donc si vous voulez ajouter une autre suite avec son propre comportement, vous n’avez aucun moyen de le faire à partir d’une ligne de commande. En 2.6, c'était le cas. Dans les versions 2.7 et supérieures, vous avez un élément appelé le protocole load_tests, qui est un crochet qui cherchera quand: le chargeur standard le recherchera lorsqu'il chargera les tests à partir d'un package ou d'un module, et qu'il l'invoquera. avec le chargeur, les tests qu’il a trouvés jusqu’à présent, et j’oublie le troisième paramètre, c’est le modèle que les utilisateurs ont donné. Quoi qu’il en soit, vous pouvez utiliser cette information pour examiner les tests et filtrer les tests que vous ne devriez pas exécuter sur une plate-forme particulière, par exemple, si vous ne voulez pas les marquer comme ignorés, ou décorer les tests en de toute façon qui a du sens. Vous pouvez donc effectuer une transformation arbitraire de votre suite de tests.
Brian: Hmm. Je dois enquêter un peu plus.
Robert: Ouais. Une des choses que vous pouvez faire, par exemple, est d’imaginer que vous avez passé un certain nombre de tests déclaratifs que vous allez écrire sous la forme de fichiers yaml. Vous pouvez les déplacer dans un répertoire et, dans la mesure où quelque part, ne doit même pas être au-dessus de celui-ci, tant que, quelque part dans votre chemin de découverte de test, il existe une implémentation load_tests qui sait comment rechercher ces fichiers, et les transformer en objets pouvant être exécutés comme des tests unitaires, ils seront alors parfaitement compatibles avec tout coureur prenant en charge le contrat standard unittest de création d’un chargeur, d’appel de découverte du chargeur, puis d’exécution du résultat. Cela vous donne donc un énorme point d’extension. Et nous avons mis cela ensemble – l’histoire de cela est que nous l’avons mise dans bazar parce que nous voulions pouvoir transformer un tas de tests dans bazaar de manière assez systématique, et nous voulions savoir comment être également compatible avec la norme. coureurs. Alors nous avons regardé autour de nous et il y avait un tas de façons ad hoc, alors, je pense que Zope avait l'idiome d'une méthode de suite de tests que le coureur rechercherait, et tordu avait son propre truc, et nous étions comme, OK, il y a donc toutes ces choses différentes, mais elles n'étaient pas assez générales, mais elles ne prenaient aucune entrée pour que vous ne puissiez pas les faire rapidement, vous savez, ce genre de chose. Nous avons donc trouvé une bonne chose, nous l'avons mis dans des outils de test, nous l'avons utilisé de manière bizarre, nous nous sommes assurés que cela fonctionne, puis je suis allé parler en amont et Michael et moi avons discuté. Il est comme ouais, bien sûr, comment cela fonctionnerait-il. Et j'ai dit, regarde, c'est exactement ce que nous faisons, et il a dit OK, cela a du sens. Il l'a ajusté un peu, pour donner plus de sens à la logique de découverte dans unittest 2, puis dans unittest 2 et dans le module Python unittest.
Robert: [10.19] Suite à cela, Michael a fini par travailler sur une énorme quantité de choses de Go pour Canonical, et a très peu de temps pour rester modeste. Alors, j’ai dit, écoutez, vous savez, vous avez besoin d’aide pour rester plus habile, et il a dit oui, ce serait génial, ici, ayez la part du commit.
Robert: C’est ainsi que je suis devenu partie intégrante du noyau de Unittest.
Brian: Alors, y a-t-il – y a-t-il – combien de personnes s'engagent pour le moins du monde, le savez-vous?
Robert: [10.43] Sur une base quotidienne, probablement environ 0,001 ou quelque chose. [laughs] Cela ne change pas très souvent.
Brian: Alors, j’ai une question sur le unittest 2, c’est un backport, non? Est-ce mis à jour? Est-ce jusqu'à 3.5, par exemple?
Robert: Ouais ouais. Alors, quand j’ai commencé à le faire, j’ai dit, vous savez, vous avez d’énormes coûts de maintenance en ce moment. Unittest 2 à ce stade n'était pas un backport roulant, il était assez vieux, alors il était gelé, comme. Unittest 2 était à l'origine du principe de la réorganisation des unités unittest et de quelques fonctionnalités supplémentaires dans un référentiel SVN distinct. Et ensuite, il a été renvoyé dans Python, dans le laps de temps de 2.7. Et puis cela a évolué dans la bibliothèque standard au fur et à mesure que les gens rencontraient des bugs, et certains d'entre eux étaient rétroportés vers unittest 2, le module externe, mais c'était plutôt incohérent. Et puis finalement, il n’a plus de ports et la bibliothèque standard n’évolue plus, et tous ceux qui utilisaient encore la version 2.7 étaient confrontés à des bogues, car les correctifs étaient passés aux versions 3.3 et 3.4 et ils ne revenaient pas à la version 2.7, et ils n’étaient plus ' t revenir au module externe. Il n'y avait donc aucun moyen d'obtenir ces correctifs.
Robert: Alors j'ai parlé à Mike, j'ai dit, regarde, tu sais, pourquoi ne pas aller plus loin que de me demander de l'aide pour la bibliothèque standard, pourquoi ne pas choisir celle-ci et en faire une version entièrement portée, nous nous contentons de tout porter et de les synchroniser, et vous n'avez pas besoin d'y penser, si vous voulez les dernières informations que vous prenez, si vous êtes satisfait de la bibliothèque standard, ce que vous voyez est ce que vous voyez. obtenir, vous pouvez simplement utiliser cela et tout ira bien, peu importe la façon dont vous allez. Et c'est ce que nous avons fait. Il y a maintenant un –Je suis un peu automatisé, je peux donc simplement extraire les commits de Mercurial, les filtrer selon l’arborescence la plus faible, les transformer en correctifs et les appliquer au référentiel unittest. Et cela le rend vraiment très facile, il suffit d’exécuter ce script, d’assurer que les tests transmettent toutes les versions de Python sur lesquelles nous souhaitons qu’il fonctionne, et de continuer à faire autre chose. C’est donc un processus assez rapide.
Brian: [12.57] Dans ce cas, recommanderiez-vous à quiconque n’utilisant pas la dernière version de Python 3.x d’utiliser unittest 2 au lieu de ce qui est intégré à leur distribution?
Robert: Absolument. Et puis, j’ai eu des discussions controversées avec des gens de la distribution à ce sujet. Donc, à mon avis, unittest 2 est le dernier unittest pour toute version de Python. Si vous utilisez PyPy, Jython, Iron Python ou Python 2.6 ou autre, utilisez-le, car il contient tous les correctifs. Il conserve la compatibilité avec le maître de Python, pas avec la dernière version. Vous serez donc en avance, la plupart du temps, par rapport à tout Python publié. L'inconvénient de ceci est que vous pouvez avoir un bogue occasionnel, où nous commettons un bogue ou si nous prenons une mauvaise décision ou quoi que ce soit, mais nous ne sommes pas dans le métier de faire des ruptures d'API pour unittest. , vous savez, nous allons le réparer.
Brian: Il y a tous ces paquets supplémentaires comme des outils de test et des agencements, et il y en a une poignée d’autres, que vous maintenez, ou du moins l’avez. Que décidez-vous de ce qui devrait être inclus dans le noyau unittest et de ce qui devrait rester dans les outils de test ou dans certains appareils, ou dans quelque chose d'autre?
Robert: [14.22] Donc, unittest n’est pas un bon endroit pour expérimenter. Le principal objectif de unittest est d'être la structure de test sur laquelle la bibliothèque standard est testée. C’est génial, mais il ya beaucoup de bibliothèques de tests externes que les gens ont beaucoup de plaisir à utiliser, des choses comme pytest et nose ont été très appréciées. Je pense que les deux sont plus populaires que testtools, par exemple. Donc, la règle générale que j'ai est la suivante: si c'est, vous savez, s'il n'y a absolument aucun doute sur le fait que c'est vraiment génial, ça devrait aller de l'avant. Si c’est un changement à simuler, c’est une copie de la bibliothèque standard, car la maquette est un flocon de neige spécial.
Brian: Oui, je voulais aussi poser une question à ce sujet. Pourquoi est-ce que Mock est un sous-paquet de Unittest?
Robert: Je ne sais pas, cette décision a été prise avant que je n’y participe vraiment. Mais c'est comme si -mock jouait au cœur d'objets Python. Il est donc très simple d'utiliser des descripteurs sur les classes lorsque vous appliquez une méthode, et des choses de ce genre, de sorte que nous ne pouvons vraiment pas dire: nous avons une API super stable. Ce n’est pas que quiconque essaiera de le casser, mais que son travail consiste à prétendre être un objet arbitraire, alors, vous savez, il n’est pas vraiment nécessaire d’avoir un lieu d’expérimentation séparé pour cela, à mon avis. Si vous savez que vous l'utilisez, vous savez que vous utilisez quelque chose sans garde-corps, donc.
Brian: OK, vas-y.
Robert: Alors oui, essentiellement, les outils de test permettent d’essayer de nouvelles choses, de nouvelles manières de structurer les objets dans le cadre de tests insignifiants ou nouveaux, et de proposer de la production, du code, des outils robustes et difficiles à reproduire. Et ce que je veux faire, c'est prendre un tas de choses que nous avons expérimentées dans les outils de test et que nous sommes convaincus d'être de bonnes choses, et les mettre au plus mal. Je n’ai pas eu le temps de le faire. J'aurais peut-être maintenant, avec le nouvel emploi, je pourrais trouver que la façon dont je partage mon temps est différente, cela pourrait être intéressant.
Brian: d'accord. Eh bien, espérons. À un moment donné, vous devrez me faire parvenir le courrier électronique de votre responsable et je lui enverrai des demandes de plaidoirie.
Robert: Peut faire.
Brian: C’est une des choses qui fait que je suis vraiment excité, en fait, je suis vraiment excité que vous ayez mentionné le fait qu’unittest 2 est un backport roulant. Je sais que cette maquette est.
Brian: Parce que je ne le savais pas et que si les gens me demandent s'ils peuvent utiliser – si différentes parties de Unittest, vous savez, je dois dire, eh bien, si vous utilisez la version 3.3, vous pouvez l'utiliser si vous utilisez 3.5…
Brian:… tu peux utiliser des choses supplémentaires
Robert: Personne ne veut faire ça, c’est trop réfléchir.
Brian: Oui, et je sais que c'est l'une des raisons pour lesquelles la bibliothèque de requêtes est maintenue en dehors du noyau Python pour la même raison, mais je suppose qu'il est trop tard pour simuler et dénaturer, ils sont déjà là, alors – mais il est logique, comme vous l'avez dit, que le noyau lui-même ait besoin d'un moyen de se tester, alors pourquoi ne pas mettre ce que vous allez utiliser ici, c'est tout à fait logique. Maintenant, de plus en plus, je suis en train d’explorer des choses supplémentaires comme le sous-test et, quels scénarios de test? Subtest semble être comme invisible, il n’ya que trois ou quatre billets de blog que j’ai trouvé et qui en parlent même.
Robert: Parlez-vous des sous-tests ou des sous-unités, car ce sont des choses totalement différentes.
Brian: Oui, des sous-tests.
Robert: Les sous-tests, c’est vrai, les sous-tests ont donc été ajoutés à la bibliothèque standard, ils sont disponibles dans unittest 2 car c’est un backport, et ils permettent de mettre en évidence l’utilisateur de plusieurs cas différents au sein d’un même test. Ainsi, par exemple, si vous testez si une fonction retourne correctement si elle est prime ou non, vous pouvez utiliser la syntaxe with subtest self.subtest, puis lui donner une liste complète de nombres, avec lesquels vous ' Je vais vérifier si ce sont des projets de premier plan, puis recommencez avec une liste complète qui ne devrait en aucun cas être premier. Et, cela vous donnera une vue plus riche sur les tests qui ont été réellement faits, comme le nombre d’affirmations que vous avez faites, et non pas sur le résumé, mais si vous avez le mode verbeux, vous verrez chacune d’elles comme un élément distinct qui ceci – trois ont été jugés et cinq ont été jugés et sept ont été jugés. De plus, il génère également des identifiants de test uniques. Ainsi, si vous avez quelque chose qui n’est pas un exemple aussi trivial, c’est un peu plus grand, vous êtes en mesure de signaler dans un cas comme Jenkins le cas spécifique qui a échoué, plutôt qu’un seul des des centaines ou des milliers de cas que nous avons générés ont échoué. Les scénarios de test sont une approche beaucoup plus précoce pour faire à peu près la même chose. J'ai mis en place des scénarios de test lorsque nous voulions faire des tests d'interface. Nous voulions donc que quatre ou cinq éléments implémentant la même interface, vous savez, aient peut-être été assez volumineux, dix, quinze, vingt méthodes et qu'il s'agisse de stockage de données, alors, vous savez, vous envoyez beaucoup de données beaucoup de données et veiller à ce qu’elles reviennent sous la forme appropriée et à ce que les transactions soient obéies et à ce genre de choses. Mais nous ne voulions pas écrire le même test cinq fois avec un type différent en haut. Nous ne voulions pas utiliser de sous-classes, car elles sont très rigides. Il est très difficile d’avoir quelque chose que vous n’avez présent que dans une sous-classe lorsque vous utilisez des sous-classes pour y parvenir. Et de plus, ils sont plutôt unidimensionnels. Je veux dire, oui, vous pouvez utiliser un héritage multiple, mais le cerveau de la plupart des gens explose assez rapidement quand vous commencez à le faire. Et donc, si vous imaginez que vous avez en réalité deux ou trois interfaces qui se retrouvent sur la même façade et que vous voulez vérifier qu’elles interagissent correctement, vous voyez que vous examinez le produit croisé de choses que vous ' Ré-test pour, ou la façon dont les tests peuvent varier. Le sous-classement n'est pas une façon claire de l'exprimer. Ainsi, les scénarios de test indiquent que nous allons créer des objets de test distincts au moment de l’exécution et que nous fournirons l’attribut d’un scénario de test qui vous indiquera l’implémentation existante ou le scénario que vous souhaitez pouvoir utiliser. à sur cette branche de votre ensemble de choses que vous comparez. Les scénarios de test diffèrent donc des sous-tests de plusieurs manières. Premièrement, les scénarios de test multiplient les objets de test eux-mêmes. Les sous-tests fonctionnent dans un seul test pour admettre des objets de sous-test individuels. Les scénarios de test demandent un nom humain pour chaque point de chaque dimension. Donc, si vous faites varier dans deux dimensions, alors vous aurez un nom humain dans la dimension un et un nom humain dans la dimension deux, et cela vous fera une belle ID avec un prénom par une virgule, et il les met dans entre crochets, et met cela à la fin de votre nom de méthode de cas de test. Tandis que les sous-tests sont implicites, ils disent simplement, oh, vous avez un ensemble de ces choses, ce sont les valeurs, donc il ne montrera que la valeur, et si vous devez vous assurer que la représentation sous forme de chaîne de la valeur est quelque chose cela a du sens pour un humain qui le regarde plus tard. Au-delà de cela, la capacité de représenter des choses de complexité équivalente avec elles est un peu similaire. Je suppose que le dernier point serait que les scénarios fonctionnent au niveau de la classe, vous paramétrez donc une classe et vous obtenez tous les tests de cette classe multipliés par les scénarios que vous avez produits. Les sous-tests fonctionnent au sein d'un seul test, donc si vous avez une classe de trente tests et qu'ils doivent tous avoir le même paramétrage, vous devrez avoir une fonction qui vous donnera les bons paramètres de générateur pour les sous-tests, et vous devrez appeler cela à partir de chacun de ces tests, donc il sera plus manuel de le décrire.
Brian: [22.21] OK, comme, je peux voir que, comme si les deux résolvaient un problème de paramétrage, mais ils – je veux dire que cela aurait un sens si vous avez besoin de quelque chose comme ça, jetez un coup d'œil aux deux et voyez lequel convient le mieux à votre cas particulier la situation alors?
Robert: Vous pourriez même potentiellement utiliser les deux dans le même projet. Ou même dans la même classe, ils devraient bien coopérer.
Brian: [laughs] OK, je vais devoir essayer ça, juste pour voir. Donc, ça va être amusant. Je me rends compte que je suis ravi de vous avoir au téléphone et de vous poser beaucoup de questions profondes, mais que je n’ai même pas, sur le podcast, discuté le moins du monde. Depuis un ou deux mois, j’essaie d’explorer plus minutieusement, de mieux comprendre, et j’apprécie mieux le projet. Cependant, je veux dire, il y a vraiment de grandes différences entre, en particulier, les plus faibles et les plus pestest.
Robert: Oui. Bien sûr sont.
Brian: Alors, avez-vous des opinions ou des réflexions sur les différences que vous voulez partager, ou simplement, ce sont toutes les deux de bonnes options?
Robert: [23.24] Eh bien, ce sont certainement les deux bonnes options. Pytest a l'avantage de suivre Python, il a vraiment développé un sentiment de ce que pythonique est. Et l’une des choses que j’aime dans pytest, c’est que l’atmosphère est très maigre, là où vous n’êtes pas – vous n’avez pas ce défaut aussi grave. Si unittest avait cette chose que cet objet, testcase, a trois API différentes. Il a l'API que vous utilisez pour exécuter le test, c'est donc comme .run et .debug, et c'est l'API que vous utilisez pour décrire un test, donc affirmez ceci, affirmez-le et ainsi de suite, puis obtenez l'API. qu’en tant qu’utilisateur, vous le mettez pour tous vos propres assistants et tout le reste. Donc, il a trois maîtres différents, et la configuration et le démontage sont un peu plus gênants dans la mesure où ils sont assis à moitié dans l’espace utilisateur et à moitié dans l’espace cadre. Si vous pensez remplacer ces méthodes, c'est un type d'implémentation de méthode de modèle si vous examinez les entrailles de l'exécution. Mais vous pouvez appeler et ne pas appeler, et le cadre ne vous dit pas si vous avez fait ce qu’il fallait. Pour Pytest, rien de tout cela n'existe. Vous n'avez pas cet objet à usages multiples qui est assis ici et qui sert tous ces différents maîtres. Donc, l’une des choses que je voudrais faire, ce qui serait assez perturbant, mais je voudrais vraiment le faire, est de trier cela, d’avoir un seul objectif pour chaque type dans le plus petit ensemble d’objets. Pour que vous vous échappiez de cette tension où vous pouvez accidentellement vous décontracter en écrivant une méthode sur votre propre classe. Cela n’a aucun sens, vraiment? Si je suis en train d’écrire mon cours, comment puis-je casser cette chose là-bas? Donc, pytest n’a aucun de ces bagages. [25.28] D'autre part, Pytest fait des choses avec lesquelles je ne suis pas très à l'aise. La façon dont il utilise une déclaration d'assertion régulière et l'introspection. Holger et moi ne voyons pas les choses en face, il dit que c’est cool, nous l’avons rendu beaucoup mieux qu’avant, et j’ai l'impression que oui, je ne suis toujours pas très optimiste à ce sujet.
Brian: Oui, donc ma question sur la réécriture des assertions n’est jamais, pas dans un avenir proche.
Robert: Je ne le mettrai pas dedans. Si quelqu'un se présente et dit que l'implémentation est vraiment propre, ce n'est pas une énorme quantité de magie noire assiégée, c'est juste un appel de fonction à une chose, c'est une aide, c'est dans la bibliothèque standard, peut-être dans le module inspect, ou le module AST ou quelque chose du genre, comme, je ne sais pas, trois lignes de code ou quelque chose du genre, et ce n'est pas obligatoire, les gens peuvent l'utiliser s'ils le souhaitent, ne pas l'utiliser si ils ne veulent pas, vous savez, je ne m'opposerais pas à ce que ça rentre. Si… la surpuissance de la maintenance va être faible, les chances de rupture et de besoin de maintenance seront réduites, et le bénéfice potentiel que les gens aimeront est élevé, excellent, faisons-le. Par contre, je ne pense pas que la réécriture d’affirmation soit un moyen particulièrement utile d’écrire des affirmations. Je n'ai pas – je n'ai pas eu une bonne expérience, et j'ai utilisé pytest pour certains projets, comme lorsque je travaille sur pip, c'est tout pytest, et vous finissez par faire des choses horribles sur les cordes multilignes à la fin d'une assertion pour écraser le message d'erreur que vous voulez, plutôt que de pouvoir déléguer ce genre de chose à quelque chose que vous pouvez partager entre beaucoup d'assertions différentes de manière beaucoup plus simple. Donc, j'aime beaucoup l'affirmateur de style hamcrest, que nous avons mis dans les outils de test et que nous voulons vraiment mettre de côté, et il y a un consensus général parmi un tas de gens de l'espace des bibliothèques standard Python que ce serait une bonne chose à faire. . Nous aimerions avoir des correspondances dans la bibliothèque standard. C’est une question de temps et une des choses que testtools fait que le test unitaire n’a pas fait pour le moment est qu’il peut attacher des données assez arbitraires à un résultat unittest. Supposons par exemple que vous avez un test qui teste le format de stockage des données d’une base de données. Vous pouvez prendre ce répertoire de base de données, le compresser et l’attacher à votre sortie la plus unitaire, et cela serait représenté sous la forme d’un objet de type MIME. Ainsi, vous saurez qu’il est binaire, vous savez combien de temps il est, vous pouvez l’envoyer dans votre processus et vous pouvez également l’exporter d’un processus à l’autre, si vous utilisez un protocole de test capable de transporter ce type de données. Et il n’ya aucune installation pour cela qui ne tienne pas. Unittest ne connaît que les traces. La possibilité d’avoir une assertion riche, votre assertion peut être une chose qui génère des données riches sur ce qui ne va pas. Donc, ces deux types de choses, si nous ne voulons pas introduire une API et la réviser peu de temps après, nous devons résoudre l’autre en premier, qui est un peu la prochaine sur ma liste de choses à faire.
Brian: d'accord. Donc, il y a certainement des améliorations à venir?
Robert: Il y a certainement des améliorations que je voudrais faire. Avoir le temps de le faire est probablement la grande question.
Brian: Oui, eh bien, c'est logique. Unittest, par exemple, est –shares un historique avec les autres frameworks de style x-unit. C’est un avantage pour les personnes venant d’autres langues bien sûr, si elles sont habituées à tester différents types de styles. Mais est-ce toujours un inconvénient que vous souhaitiez ne pas partager tout cet héritage avec, disons, Junit et d’autres?
Robert: Non, au cœur de la problématique, le problème de la structure en unités x est qu’elle favorise l’héritage par rapport à la composition, plutôt que de privilégier la composition par rapport à l’héritage. Donc, il n’ya pas de moyen direct d’écrire des unittests composés plutôt que des unittests hérités en soi-même. Maintenant, si vous regardez des choses comme les fixtures et les correspondances, cela vous laissera respectivement décrire les choses que vous voulez pour votre test et décrire les assertions riches que vous voulez avoir, sans nécessiter une hiérarchie de classes liée à vos tests. Vous pouvez vous en sortir, vous pouvez commencer à écrire de petites choses ciblées et à les composer comme vous le souhaitez. Donc, vous pouvez le faire en collaboration avec le framework le plus modeste, mais cela ne se prête pas à cela. C’est donc l’un des inconvénients les plus importants de la plupart des structures x-unit. Cela dit, vous savez, cela a créé la révolution des tests unitaires que nous avons aujourd'hui, il est devenu gérable et abordable, et il est très simple de s'asseoir et d'écrire un framework x-unit, il n'y a pas grand-chose à faire, car Vous avez un petit nombre de pièces mobiles et vous n’avez pas besoin de fonctionnalités linguistiques particulières pour le faire, donc c’est un peu un –qu’il donne, mais il enlève.
Brian: d'accord. Pytest a beaucoup de crochets que vous pouvez mettre en place pour l’étendre, alors que le principal moyen d’étendre unittest consiste à sous-classer, ou à partir des différentes parties. Y a-t-il jamais – y a-t-il une raison de ne pas mettre les crochets à différents endroits? Ou est-ce juste la recommandation de la sous-classe?
Robert: [30.47] Les crochets actuels dépendent donc de cette possibilité de substituabilité. Vous mettez quelque chose en place qui a le même comportement, cela fonctionnera toujours, de toute façon, dans l’aspect le plus modéré de Python, car c’est un système de frappe de canards. Donc, vous n’avez pas besoin de sous-classe, mais la partie facile est de sous-classe, donc je suppose que c’est ce que j’essaie de comprendre. Y a-t-il une raison d'avoir ou d'éviter d'autres crochets? Non, vous pouvez certainement installer d’autres crochets. Je ne voudrais cependant pas avoir des crochets de portée mondiale. Je souhaiterais donc que des éléments s’intègrent dans le cycle de vie d’un programme de test et ne vous interdisent pas d’utiliser l’API dans un programme plus volumineux. Donc, vous pouvez maintenant avoir deux suites de tests différentes dans deux threads unittest différents et les exécuter, et il n’ya que très peu de choses qui ne fonctionneront pas correctement.
Robert: [31.43] Les choses qui ne fonctionneront pas correctement sont des choses comme la capture standard, qui dépend de la correction de singe…
Brian: Oh oui
Robert:… erreur standard et erreur standard. De toute évidence, ces choses ne fonctionneront pas très bien, car ce sont des globals, mais rien d’autre n’est global. Ainsi, le comportement du chargeur n’est pas global, il est paramétré par les paramètres que vous donnez au chargeur lors de sa création. Le comportement des classes individuelles est paramétré en ajoutant des attributs à la classe. Toutes ces choses ont donc une portée locale et c’est une bonne chose. Ce que vous voulez généralement faire lorsque vous souhaitez avoir des points d'ancrage vous permettant de faire autre chose, est d'appliquer quelque chose de façon semi-globale. Vous allez donc l'appliquer sur tout le contexte d'une suite de tests, mais vous ne le souhaitez pas. effectivement être global dans le processus Python, c’est la distinction que j’essaie d’établir.
Robert: Je souhaite donc conserver la possibilité d'utiliser unittest comme une bonne bibliothèque, d'être un citoyen avisé de la bibliothèque dans tout ce que nous faisons dans l'avenir. That said, I am keen to have some more hooks that give a really clearly defined lifecycle, just need to find a way of doing it following this pattern.
Brian: OK, that makes sense. You’ve used pytest, then, sounds like?
Robert: Yeah, absolutely.
Brian: Pytest –the pytest fixture model is very different than setup and teardown. It does feel different when you’re using it. I was just cursory looking at the fixtures package, there’s test resources and there’s testtools.fixtures, are those all sort of the same thing? And do they relate any way to pytest-like fixtures?
Robert: There’s also Chris Withers’ testfixtures and there’s a bunch more out there. So, the history of the ones –so the ones you mentioned are all ones that I’ve spun up. Testtools.fixtures is just the fixtures that testtools itself has. Fixtures is a standalone library that defines the fixtures contract. And a fixture is essentially a super context manager.
Robert: So, a standard context manager, you can enter it, you can exit it, you can enter it again, you can exit it again, and that’s really useful. So if you think of a context manager that gives you a working postgreSQL database, that’s a good thing…
Robert: …for testing, right, you just go with my database, do some stuff. But from a testing perspective, you often want a bit more. One of the things that you want in a big test suite is, maybe you want to reset to a blank slate, but you don’t want to get rid of everything. So if you think about a postgreSQL database, maybe half of your setup time is initialising the server and getting the process running, and then a small fraction of it is actually creating the database you’re gonna do this test in. So, one way of structuring it would be to have two separate fixtures, one for the database, and one –sort of the server one for the database, and the –when you exit the database one, you can enter it again to get a new one, but it depends on that server still running. And that would be OK, but in actual fact you can get another performance optimization by not throwing away a test database, and instead resetting a bunch of the internal parameters. That like –this is what happens if you talk to a database administrator and say please make my test suite fast. And it’s fantastic, but you’re not actually throwing away that test database. So, fixtures introduces a reset concept where you can say I want to start over, but I don’t want to tear you down and bring you back up again. So you can take a shortcut, you can be faster if that’s possible for you.
Robert: And you also want the ability, if something goes wrong, and you’ve got multiple, like a graph of things that you’re using, it’s nice to be able to report on all the things that went wrong, not just the first one which required some different –so there’s a bunch of little stuff like that.
Brian: So those –so far that sounds a lot like the pytest fixture model.
Robert: [35.12] So, I believe that pytest fixtures were inspired by the testtools fixtures.
Robert: But not derived from. So they said hey that’s a good idea, and then did something that worked well in their context. And I think that’s completely sensible.
Brian: One of the benefits of coming second.
Robert: Yeah, absolutely. You know, look, if I can sit down and say hey pytest has got a whole bunch of wonderful stuff and some of those things were based on stuff I did, that’s cool, you know. Everyone’s winning.
Brian: Whether an exception is an assert or any other type of exception, really matters in unittest, and it does not matter in pytest.
Robert: [35.52] Yeah, so I mean one of the things there is to –you should never use teardown. You should never ever use teardown. You should use clean-ups.
Brian: Yeah, I agree. Because, you can’t really do a bunch of things in teardown and have them all happen.
Robert: So there’s that, but there’s also that if teardown –if setup fails, because of an exception, any class of exception, if setup fails, teardown doesn’t run at all.
Robert: But cleanups do.
Brian: Oh they do?
Brian: Cleanups will run even if the setup fail.
Brian: Ah, OK. So you’d have to add the cleanup at the point where you know that there’s something to clean up.
Brian: Yep, OK.
Robert: I mean, that’s the best practice. I mean, arguably Python can be interrupted between any two bits of bytecode, right, by control-c, so generally, you’d add the cleanup right before you start using the resource. Say you are going to make a temporary directory. That one’s really hard to do safely. It’s almost impossible to not leak a temporary directory in Python. I need to do a blog post on this, how ridiculously hard it is to do this correctly. It’s very easy to not leak it in the common case, but just making sure someone can hit control-c between any two bits of bytecode, that’s the hard thing.
Brian: Yeah, yeah definitely. So, I guess, the other bit was I really hate the name unittest, because people believe its just for unit testing. And it works just fine for all levels of testing.
Robert: Yep, absolutely agree. And, I mean, unit test isn’t even a well-defined thing. Some people will tell you it only tests a single bit of code, but then they are using classes from everywhere that have dependencies tangled throughout their whole code base and things, and the other side of the program can cause it to fail, its not really testing just that one thing, and the other can say it is, because I’ve mocked everything out, and then you go, so, really you’ve got no idea if your code works or not, because you’ve got mocks that don’t tell you anything about what’s happening.
Robert: You know, they make the assumption that that thing over there hasn’t changed, how —
Brian: Yeah that’s a nightmare. I don’t even go there anymore, cos I think people that believe they can mock everything and have a workable system are just smoking something [38.21] Lot to think about here. I actually –I’m gonna go ahead and just assume this is just part one, I’m definitely gonna have to get you on again.
Robert: [laughs] Sure thing.
Brian: Pick your brain. Unittest is just in the standard library, but is there –how about you? Is there a place for people to find out more about you or get a hold of you if they need to, or?
Robert: Err, the testing in Python list is probably the best place to grab hold of me for things about Python testing. I’m also on the Python testing IRC channel.
Robert: I mean if its something to do with a specific project, talk in that project’s forum is usually the rule of thumb with open source, but I try and be pretty approachable. My IRC nickname is lifeless, so you can get me there, or at RBT Collins on twitter, and I’m happy to talk, you know, just about anything.
Brian: OK. Before we go off the virtual air, anything you want to cover that we haven’t already?
Robert: So, I think, no. Like, go out and write tests, that’s an incredibly useful thing to do. Probably the only thing I’d say is, you know, don’t be afraid to fix bugs in unittest. It’s not static, we take patches. And if they don’t get responded to quickly on the Python bugtracker, come and ping me. I get busy and I don’t always go and look at it as often as I should and I’m happy to be reminded that there’s something there that needs a review.
Brian: Alright, thanks a lot, and we’ll hopefully schedule another one sometime after I absorb all of this information.
Robert: Sounds great Brian, thank you, thank you for having me.
music: Wow, what an interview, right? I hope that rattle noise wasn’t too annoying. If you’re an audio geek and would be interested in partnering with me to make the podcast even better, I’d love to talk with you about it. Show notes and links are at pythontesting.net/19. Special thanks to my wonderful Patreon supporters, visit patreon.com/testpodcast. And again, Patreon is P-A-T-R-E-O-N. If you’re already a supporter, thank you. If you’re not supporting it, please consider it. Thanks a lot for listening, and get out there and test some code. I hope you enjoyed it. Merci. [music]