Mamba est un outil de Behaviour-Driven Development pour écrire des spécifications en Python.
Les spécifications d'un programme, ou juste « spéc’ », sont des exemples de comportements que le programme doit avoir. Elles peuvent être exécutées pour vérifier que le programme a bien le comportement attendu.
Pourquoi j'utilise Mamba ?¶
Là où Pytest utilise des classes et de fonctions pour organiser les tests, Mamba utilise
des gestionnaires de contexte (with
). Ceci permet d'imbriquer autant de niveaux que l'on
souhaite. Par exemple, pour une classe (niveau 1) on va décrire une méthode (niveau 2) et ses
différents comportements (niveau 3). On peut même rajouter des contextes différents (niveau 4).
L'autre avantage des gestionnaires de contexte, c'est qu'ils utilisent toujours les mêmes mots
clés : describe
et it
. Il n'y a donc pas besoin de se creuser la tête à écrire un nom
de test qui respecte les conventions de nommage des fonctions en Python. Et là où cela
devient pratique, c'est que ces gestionnaires de contexte acceptent une chaîne de caractères
en argument. Et ça, c'est du texte libre ! On peut écrire ce que l'on veut, comme on veut !
En pratique, une spéc’ ressemble à cela :
from mamba import describe, it
from robber import expect
# …
with describe(MyClass):
with describe(MyClass.my_method):
with it("does something"):
assert MyClass().my_method()
with it("does something else"):
assert MyClass().my_method()
En exécutant la spec’, on obtient la sortie suivante :
$ mamba --format documentation
MyClass
my_method
✓ it does something
✓ it does something else
Ça a plutôt de la gueule, non ?! Le seul petit bémol est que, si l'on utilise
du texte en français, alors on a des it
en début de phrase alors qu'on voudrait
des il
. Mais le l
et le t
se ressemblant un peu, ça ne pique pas (trop) les yeux.
Pourquoi je veux le remplacer ?¶
Je suis plutôt satisfait de Mamba, mais, malheureusement, j'ai rencontré un problème qui m'a fait me demander si je ne devrais pas trouver un alternative.
Mamba n'est plus maintenu depuis novembre 2020. Ce n'est pas à proprement parler
un problème, car il « juste marche » et « fait le taf’ ». Mon problème est qu'il
a comme dépendance args
dont le paquet est cassé sur ma distribution
(Guix, j'y reviendrai dans un article dédié !).
Il n'est donc plus possible d'installer Mamba sur ma distribution ! Cela n'est pas
vraiment un problème en phase de développement, car c'est Poetry qui installe
les dépendances, mais cela le devient lors de la création de paquet. En effet, en tout
cas dans Guix, une des étapes de la création de paquet et le lancement des tests.
Cela nécessite d'installer les dépendances de test. Ceci n'est pas possible tant que
le paquet python-args
est cassé !
Par quoi le remplacer ?¶
pytest-describe
+ pytest-spec
¶
D'après le dépôt du projet, le dernier commit est de novembre 2021. De tous les candidats, c'est donc le « mieux » maintenu.
Il est possible d'avoir une description textuelle du test via la docstring, mais on doit quand même trouver un nom de fonction pour le test. Il est possible d'avoir plus de 2 niveaux d'imbrication (plusieurs niveaux de fonctions).
pytest-spec est un formateur de sortie pour Pytest qui améliore l'affichage pour les spécifications.
Voici un exemple de code :
# …
def describe_MyClass():
"""not used!"""
def describe_my_method():
"""not used!"""
def spec_1():
"""it does something (docstring)"""
assert MyClass().my_method()
def spec_2():
"""it does something else (docstring)"""
assert MyClass().my_method()
Une fois Pytest configuré avec spec_test_format = {result} {docstring_summary}
,
la sortie associée est :
$ pytest --spec describe_test.py
describe_test.py:
MyClass:
My method:
✓ it does something (docstring)
✓ it does something else (docstring)
pytest-pspec
¶
D'après le dépôt du projet, le dernier commit est de juin 2020.
C'est un formateur de sortie. La structure des tests est donc la structure classique de Pytest.
On peut avoir une description textuelle via une docstring, mais on doit quand même trouver un nom de fonction pour le test.
Voici un exemple de code :
# …
class TestMyClass():
"""MyClass (docstring)"""
def test_spec_1(self):
"""it does something (docstring)"""
assert MyClass().my_method()
def test_spec_2(self):
"""it does something else (docstring)"""
assert MyClass().my_method()
Et la sortie associée :
$ pytest --pspec pspec_test.py
pspec_test.py
MyClass (docstring)
✓ it does something (docstring)
✓ it does something else (docstring)
pytest-it
¶
D'après le dépôt du projet, le dernier commit est de janvier 2020.
C'est un formateur Pytest, il n'y a donc que 2 niveaux d'imbrication, mais on peut contourner cette limitation ajoutant des marks. Mais il faut faire pas mal de copier/coller pour obtenir le résultat souhaité !
On peut avoir une description textuelle via une docstring, mais on doit quand même trouver un nom de fonction pour le test.
Voici un exemple de code :
from pytest import mark as m
# …
@m.describe("MyClass (mark)")
class Test:
@m.describe("my_method (mark)")
@m.it("does something (mark)")
def test_1(self):
assert MyClass().my_method()
@m.describe("my_method (mark)")
@m.it("does something else (mark)")
def test_2(self):
assert MyClass().my_method()
Et la sortie associée :
$ pytest --it it_test.py
* it_test.py...
- Describe: Myclass (mark)...
- Describe: My_method (mark)...
- ✓ It: does something (mark)
- ✓ It: does something else (mark)
Conclusion¶
Des 3 alternatives étudiées, c'est la combinaison pytest-describe
+ pytest-spec
qui est la plus satisfaisante, même s'il est toujours nécessaire de trouver
des noms pour chaque fonction de test, en plus de leur docstring.
Après avoir jeté un coup d'œil à « la concurrence », je me dis que Mamba n'est pas si mal que ça ! Je pense que je vais donc continuer de l'utiliser pour écrire mes spéc’.
Pour ce qui est de mon problème de création de paquet sur ma distribution… et bien je vais me retrousser les manches et mettre à jour la définition du paquet ! Après tout, j'ai choisi Guix aussi parce qu'il est facile de contribuer des définitions de paquet !