Une révolution dans le monde des tests

410e3353165c33043ab69be7fc366428?s=47 Boris Feld
October 06, 2018

Une révolution dans le monde des tests

Depuis plusieurs années, les outils de qualité ont évolués, les outils de CI sont de plus en plus scalables, les librairies d'écriture de tests sont plus nombreuses et ont bien mûries et de nouveaux outils sont apparus pour améliorer encore plus la qualité du code que l'on produit.

Néanmoins, la plupart des outils de CI se contentent de lancer un script et de vérifier le code de retour, la plupart des librairies de tests nous imposent de choisir entre lancer l'ensemble de nos suites de tests ou s'arrêter pour voir le détail du test en échec et enfin la plupart des innovations des outils de CI, l'exécution en parallèle et l'exécution à distance, n'ont pas encore trouvé le chemin jusqu'au développeurs.

Comment améliorer la situation ? Je vous présenterai l'un de mes projets, LITF (https://github.com/Lothiraldan/litf) un nouveau format d'entrée et de sortie pour les librairies de test et BALTO (https://github.com/lothiraldan/balto), un orchestrateur de tests utilisant ce nouveau format. BALTO est écrit en Python 3.6 / Asyncio et supporte bien entendu Pytest comme première librairie compatible.

Grâce à ce nouveau format, BALTO peut exécuter plusieurs suites de tests dans des langages différents, à distance sur un clusteur Kubernetes et le tout en parallèle. En tout cas, c'est le but pour la version stable.

410e3353165c33043ab69be7fc366428?s=128

Boris Feld

October 06, 2018
Tweet

Transcript

  1. 1.

    UNE RÉVOLUTION DANS LE MONDE DES TESTS UNE RÉVOLUTION DANS

    LE MONDE DES TESTS Boris Feld - PyConfr 2018 octobus.net
  2. 3.

    BORIS FELD BORIS FELD Expert Mercurial, Python et DevOps. Enthousiaste

    sur la qualité. https://octobus.net/ @lothiraldan
  3. 7.

    VOUS N’ÊTES PAS LES SEULS VOUS N’ÊTES PAS LES SEULS

    2017 2018 0.00% 10.00% 20.00% 30.00% 40.00% 50.00% 60.00% 70.00% 80.00% Launching tests Writing tests => 70% lancent des tests
  4. 10.

    UNITTEST UNITTEST Inclut dans Python Permet d’écrire des tests sous

    la forme de classes Fournit des assertions sous la forme de méthodes d’assertions.
  5. 11.

    UNITTEST EXEMPLE UNITTEST EXEMPLE import unittest class TestStringMethods(unittest.TestCase): def test_upper(self):

    self.assertEqual('foo'.upper(), 'FOO') def test_isupper(self): self.assertTrue('FOO'.isupper()) self.assertTrue('Foo'.isupper()) if __name__ == '__main__': unittest.main()
  6. 12.

    EXECUTION AVEC UNITTEST EXECUTION AVEC UNITTEST F. ================================================================= ===== FAIL:

    test_isupper (__main__.TestStringMethods) ----------------------------------------------------------------- ----- Traceback (most recent call last): File "test.py", line 10, in test_isupper self.assertTrue('Foo'.isupper()) AssertionError: False is not true ----------------------------------------------------------------- ----- Ran 2 tests in 0.000s FAILED (failures=1)
  7. 13.

    NOSETESTS NOSETESTS Un unittest meilleur Un ecosystem de plugin Une

    vraie avancée Mais deux paquets Pypi, nose et nose2 Un écosystème parcemé
  8. 14.

    EXECUTION AVEC NOSETESTS EXECUTION AVEC NOSETESTS F. ================================================================= ===== FAIL:

    test_isupper (test.TestStringMethods) ----------------------------------------------------------------- ----- Traceback (most recent call last): File "/home/lothiraldan/Labo/presentations/balto_revolution_fr/src/tes t.py", line 10, in test_isupper self.assertTrue('Foo'.isupper()) AssertionError: False is not true ----------------------------------------------------------------- ----- Ran 2 tests in 0.000s FAILED (failures=1)
  9. 15.

    REDNOSE (PLUGIN NOSE1) REDNOSE (PLUGIN NOSE1) F. ================================================================= ===== 1)

    FAIL: test_isupper (test.TestStringMethods) ----------------------------------------------------------------- ----- Traceback (most recent call last): test.py line 10 in test_isupper self.assertTrue('Foo'.isupper()) AssertionError: False is not true ----------------------------------------------------------------- ------------ 2 tests run in 0.001 seconds. 1 FAILED (1 test passed)
  10. 16.

    PYTEST PYTEST La vraie étoile montante du marché Un meilleur

    unittest Tests sous forme de fonctions Fixtures De nombreux utilitaires De nombreux plugins
  11. 17.

    EXECUTION AVEC PYTEST EXECUTION AVEC PYTEST ============================= test session starts

    ============================== platform linux2 -- Python 2.7.15, pytest-3.1.2, py-1.5.3, pluggy- 0.4.0 rootdir: /home/lothiraldan/Labo/presentations/balto_revolution_fr/src, inifile: collected 2 items test.py F. =================================== FAILURES =================================== ________________________ TestStringMethods.test_isupper ________________________ self = <test.TestStringMethods testMethod=test_isupper> def test_isupper(self): self.assertTrue('FOO'.isupper()) > self.assertTrue('Foo'.isupper())
  12. 20.

    MA LISTE AU PÈRE NOËL MA LISTE AU PÈRE NOËL

    Couleur Barre de progression Lancer uniquement les tests échoués Lancer un test donné Interface web
  13. 29.

    QUESTION POUR UN CHAMPION QUESTION POUR UN CHAMPION C’est quoi

    Balto ? Un plugin pytest Une interface web Un serveur websocket Un lanceur de processus
  14. 34.

    LE SECRET LE SECRET Le Secret de Balto c’est qu’il

    lit la sortie standard du processus lancé
  15. 35.

    L’AUTRE PARTIE DU SECRET L’AUTRE PARTIE DU SECRET Mais il

    faut que le plugin, balto-server et balto-ui puissent se comprendre
  16. 37.

    SORTIE STANDARD DE PYTEST SORTIE STANDARD DE PYTEST ============================= test

    session starts ============================== platform linux2 -- Python 2.7.15, pytest-3.1.2, py-1.5.3, pluggy- 0.4.0 rootdir: /home/lothiraldan/Labo/presentations/balto_revolution_fr/src, inifile: collected 2 items test.py F. =================================== FAILURES =================================== ________________________ TestStringMethods.test_isupper ________________________ self = <test.TestStringMethods testMethod=test_isupper> def test_isupper(self): self.assertTrue('FOO'.isupper()) > self.assertTrue('Foo'.isupper()) E AssertionError: False is not true
  17. 38.
  18. 39.

    JUNIT.XML JUNIT.XML Basé sur XML Un gros chier à la

    n du build Format lié à Junit => Non streamable
  19. 40.

    TAP TAP Très connu dans la communauté Perl Très simpliste,

    peu extensible Format lié à TAP Perl => Nécessite un parseur en Python et en JS
  20. 41.

    MOZLOG MOZLOG Format utilisé dans les tests Mozilla 1 au

    début et à la n de chaque test => Avoir une atomicite, 1 test == 1 ligne est plus simple
  21. 42.

    LISTE AU PÈRE NOËL LISTE AU PÈRE NOËL Simple à

    écrire et lire Streaming pour pas attendre la n du run Format indépendant d’une implémentation
  22. 44.

    DE QUOI ON AS BESOIN? DE QUOI ON AS BESOIN?

    Nom du test Le résultat du test (failed, passed, skipped…) Error en cas d’erreur
  23. 45.

    EXEMPLE EXEMPLE { "test_name": "test_success", "outcome": "passed" } { "test_name":

    "test_failed", "outcome": "failed", "error": { "humanrepr": "def test_fails():\n> assert False\nE assert False\n\ntest_func.py:9: AssertionError" } }
  24. 46.

    PIÈCE MANQUANTE PIÈCE MANQUANTE { "test_number": 6, "_type": "session_start" }

    { "failed": 3, "total_duration": 0.03, "_type": "session_end", "skipped": 0, "error": 0, "passed": 3 }
  25. 47.

    PLUS DE DONNÉES PLUS DE DONNÉES Temps Messages de log

    Sortie standard et sortie standard d’erreur Diff de text ou d’images Fichier, ligne, etc…
  26. 49.

    SORTIE VS ENTRÉE SORTIE VS ENTRÉE On as aussi besoin

    d’un format d’entrée: { "collect_only": true } { "files": ["file1.py", "file2.py"], "nodes": ["file1.py::test_func"] }
  27. 50.

    COMMENT RÉCUPÉRER UN IDENTIFIANT? COMMENT RÉCUPÉRER UN IDENTIFIANT? Ajoutons un

    nouveau champ de sortie: { "test_name": "test_success", "outcome": "passed", "_id": "test_func.py::test_success" }
  28. 52.

    EXEMPLE DE SORTIE EXEMPLE DE SORTIE { "test_name": "test_failed", "_id":

    "test_func.py::test_success", "outcome": "failed", "error": { "humanrepr": "def test_fails():\n> assert False\nE assert False\n\ntest_func.py:9: AssertionError" } }
  29. 53.

    ET SI ON ALLAIT PLUS LOIN ? ET SI ON

    ALLAIT PLUS LOIN ? Et si on faisait un plugin nosetests pour ce format?
  30. 54.

    ET SI ON ALLAIT ENCORE PLUS LOIN ? ET SI

    ON ALLAIT ENCORE PLUS LOIN ? Et si on faisait un plugin pour un test runner dans un autre langage?
  31. 55.

    JUSQU’AU PEUT-ON ALLER ? JUSQU’AU PEUT-ON ALLER ? Et si

    on lisait la sortie standard d’autre chose qu’un process ? Comme une commande ssh ou un container Docker ?
  32. 56.

    VERS L’INFINIE ET AU-DELÀ VERS L’INFINIE ET AU-DELÀ Balto etant

    base sur Asyncio, et si on lancait plusieurs suites pytest en parallelle ? Ou plusieurs suites dans des langages differents sur une machine distante avec Docker?
  33. 59.

    UN NOUVEAU PROTOCOLE UN NOUVEAU PROTOCOLE Similaire à HTTP ou

    LSP, c’est une fondation au dessus duquel construire les nouveaux outils de demain.
  34. 60.

    EXEMPLE EXEMPLE Vous voulez un dashboard curses pour lancer vos

    tests? Vous pouvez ! Vous voulez un rapport sur les temps de chacun de vos tests? Vous pouvez !
  35. 63.

    INSTALLER BALTO INSTALLER BALTO Télécharger la dernière release sur Github:

    https://github.com/Lothiraldan/balto/releases Le mettre dans le path Installer le plugin litf de votre test runner Pro ter
  36. 64.

    QUE POUVEZ-VOUS FAIRE ? QUE POUVEZ-VOUS FAIRE ? Utiliser Balto

    Parler de Balto autour de vous Parler de LITF au mainteneur de votre test runner préféré