Logilab bottom bottom right

Python Meetup octobre 2016 les tests en python

Introduction

Meetup Python Nantes - mai 2016

Arthur Lutz (Logilab) @arthurlutz @logilab

Plan

  • introduction aux tests unittaires
  • lancer les tests : unittest de base, py.test, nose, pytest, etc.
  • tox pour lancer les test dans des virtualenvs
  • l'intégration continue avec python (jenkins, travis, etc.)
  • les tests en prod : healthchecks au cœur de l'application

introduction aux tests unitaires

En python

assertEqual(a, b)
assertNotEqual(a, b)
assertTrue(x)
assertFalse(x)
  • ne pas utiliser assert (assertTrue à la place)

Écrire un test

  • classes héritant de unittest.TestCase
  • toutes les methodes commencant par test sont considérées comme un test unittaire

Exemple (de la doc)


    import unittest

    class TestStringMethods(unittest.TestCase):

        def test_upper(self):
            self.assertEqual('foo'.upper(), 'FOO')

        def test_isupper(self):
            self.assertTrue('FOO'.isupper())
            self.assertFalse('Foo'.isupper())

        def test_split(self):
            s = 'hello world'
            self.assertEqual(s.split(), ['hello', 'world'])
            # check that s.split fails when the separator is not a string
            with self.assertRaises(TypeError):
                s.split(2)

    if __name__ == '__main__':
        unittest.main()

Avant / après les tests

https://en.wikipedia.org/wiki/Test_fixture#Software

  • def setUp()
  • def tearDown()
  • setUpClass et tearDownClass
  • setup_method et teardown_method
  • setup_function et teardown_function

Extensions

Mock

mock or not to mock (or where to mock?)

https://en.wikipedia.org/wiki/Mock_object


   >>> from unittest.mock import MagicMock
   >>> thing = ProductionClass()
   >>> thing.method = MagicMock(return_value=3)
   >>> thing.method(3, 4, 5, key='value')
   3
   >>> thing.method.assert_called_with(3, 4, 5, key='value')

Mock encore

  • decorator @patch
  • context manager with Patch...
  • quelques exemples d'assertions
assert_called_with
assert_called_once_with
assert_any_call
assert_has_calls
assert_not_called
reset_mock

Ne pas mocker

platform specific 1/2

platform specific 2/2

Selenium

pip install selenium

Selenium exemple


  import unittest
  from selenium import webdriver

  class AfpyTestCase(unittest.TestCase):

    def setUp(self):
        self.browser = webdriver.Firefox()
        self.addCleanup(self.browser.quit)

    def testPageTitle(self):
        self.browser.get('http://nantes.afpy.org/')
        self.assertIn('Python-Nantes', self.browser.title)

  if __name__ == '__main__':
    unittest.main(verbosity=2)

lancer les tests

https://wiki.python.org/moin/PythonTestingToolsTaxonomy

  • unittest de base
  • unit2
  • pytest (logilab-common)
  • py.test (next pytest)
  • nose & nose2
  • etc

unittest.main

En bas de chaque fichier python :

if __name__ == '__main__':
   unittest.main()

Pour le lancer :

# python test_this.py

pytest (logilab-common) historique

  • testlib - extension de unittest
  • pytest - lanceur de tests
  • selection tags avec des decorateurs (tags)
  • supprimé au profit de nouveaux projets

unit2

unittest2 est un backport python2 du nouveau unittest python3

  • unit2 == python -m unittest2
  • unit2 discover

py.test

$ pip install pytest

py.test

  • py.test
  • py.test -x : s'arrête à la première erreur
  • py.test --maxfail=3 : s'arrête au bout de 3 erreurs

py.test selection

  • py.test test_module.py
  • py.test test_mod.py::test_func
  • py.test test_mod.py::TestClass::test_method

py.test affichage

  • py.test --tb=long # the default informative traceback formatting
  • py.test --tb=native # the Python standard library formatting
  • py.test --tb=short # a shorter traceback format
  • py.test --tb=line # only one line per failure

py.test avancé

nose & nose2

Commandes :

  • nosetests
  • nose2

tox pour lancer les test dans des virtualenvs

https://tox.readthedocs.io/en/latest/

# content of: tox.ini , put in same dir as setup.py
[tox]
envlist = py26,py27
[testenv]
deps=pytest       # install pytest in the venvs
commands=py.test  # or 'nosetests' or ...

tox est amour

Accélerer tox : caches

L'intégration continue avec python

Jenkins

Matrix Project

Junit / xUnit

Quand lancer les tests ?

  • Sur chaque pull request ?
  • sur chaque tête
  • avant un push ? avant un commit ?
  • sur l'ensemble ?
  • sur quel sous-ensemble ?

les tests en prod : healthchecks au cœur de l'application

Inspirations

healthchecks - principe

  • lancer des tests directement au coeur de l'application en production
  • complément de la supervision traditionnelle
  • permet de différentier entre "le process tourne" et "le service répond"
    • particulièrement utile pour le déploiement continue et la répartission de charge dynamique

healthchecks en python

Fin