pytest et conftest à rallonge

Bonjour,

Rien que sur mes petits “projets”, je finis par avoir un conftest.py de plusieurs centaines de lignes et des noms de pytest.fixture qui ne veulent rien dire.

Je suis à la recherche de ressources (idéalement, PAS des tutoriels vidéo) sur les bonnes pratiques autour de pytest et conftest
et plus particulièrement sur la gestion des fixtures.
Par exemple :

  • Comment organiser ceux-ci (dans des fichiers et/ou une arborescence ?) plutôt que de les laisser dans un conftest.py à rallonge.
  • Est-il pertinent d’utiliser le même fixture pour plusieurs tests ?
  • Quelles règles de nommage adopter ?

Si vous avez des idées, des conseils de lecture, et pourquoi pas des exemples de projets qui ont mis en place de trucs autour de conftest (Si en plus, je peux aider à des trucs simples pour progresser autour de pytest et du reste), n’hésitez pas :slight_smile:

Je n’ai pas de ressource en tête…

TL;DR : Oui !

Laisse-moi tenter de m’expliquer avec un exemple caricatural d’un projet avec une base de donnée, comme fixture tu peux avoir de la donnée à charger en base de donnée (qq utilisateurs, éventuellement avec des droits différents, et quelques valeurs “basiques” pour chaque table, …). Avec une petite fixture comme ça on peut déjà rédiger énormément de tests, ce n’est pas grave si chaque test n’utilise pas toute la donnée chargée, il profite juste de ce qui est là.

Ça rend même les tests assez triviaux à écrire et lire une fois qu’on connaît les données de la fixture car c’est toujours les mêmes. Il faut par contre faire un effort sur le nommage, si tes utilisateurs dans ta fixture c’est “Bart”, “Lisa”, “Homer”, etc… Homer a les droits d’admin, Lisa a les droits “profs” et les autres sont de simples utilisateurs, c’est marrant, mais pour quelqu’un qui relit les tests c’est un truc de plus à apprendre par cœur. C’est pas dur. Mais c’est un effort. Alors que si tes utilisateurs c’est “user1” “user2”, “teacher1” et “admin1”, c’est transparent, tout le monde peut lire un test sans aucune culture préalable de ta “fixture initiale”.

Parfois ce genre de fixture n’est pas directement réutilisable et tu peux soit construire une nouvelle fixture en t’appuyant dessus (une fixture pouvant prendre une fixture en paramètre), soit si c’est vraiment à usage unique ton test peut repartir de la fixture de base et juste faire la petite modif nécessaire avant de commencer à tester.

Attention à ne pas s’engouffrer dans “ces petites modifs nécessaires” qui ont tendance à polluer les tests, s’il y en a trop, s’il y en a partout, ou si ces modifications ne sont pas du code trivial à lire. Attention à ne pas non plus introduire dans la fixture de base des cas ultra-particuliers nécessaire à un seul test, ça la rendrait moins lisible, on se demanderait très vite à quoi sert quoi, alors que “admin1”, “user1”, “user2” on se doute bien de leur utilité.

Si tu peux nous partager ton code, peut-être que ça peut nous inspirer pour te donner d’autres conseils, je veux bien partager, mais j’ai pas grand-chose d’intéressant, sniff :

2 « J'aime »

Merci beaucoup pour cette réponse détaillée.
Je vais pouvoir améliorer ma façon de faire. Particulièrement sur les valeurs des données (les fameux “Bart”, “Lisa” et “Homer”) et sur la réutilisation des fixtures. À la réflexion, je pense que cela pourrait aussi m’aider à moins galérer sur le nommage de celles-ci puisque moins nombreuses et plus “génériques”.
J’ai commencé à regarder tes trois exemples et je n’ai pas encore tout compris, mais c’est inspirant, car cela m’ouvre des possibilités auxquelles je n’avais pas pensé jusqu’ici.

Je réfléchis de plus en plus à publier mon code sur un dépôt public (pas facile quand on débute, il faut oser :wink: ) pour pouvoir solliciter des retours / conseils.

Sauf si tu as une bonne raison de ne pas publier (genre « c’est interdit par mon chef, si je publie je me fais virer. ») je t’encourage à publier.

Chez jeune-moi ça augmentait même la qualité du code, de la même manière que travailler en équipe. Je ne code (codais ?) pas pareil quand « c’était juste pour moi planqué » ou quand « la terre entière peut me juger » (ou juste un collègue, ça marche aussi). J’ignore si je fais autant la différence aujourd’hui (il n’y a quasiment rien que je ne publie pas, je crois).

Le nombre de fois où mon cerveau m’a dis « non, tu ne peux pas commit ça, je suis sûr qu’on peut faire plus propre, attends… » sur des repos où je ne suis pas seul est incroyable. J’en déduis que sur du code sur lequel je suis le seul à travailler mon cerveau doit être dans une mode « YOLO » pas très propice à la qualité (j’imagine, car j’ai pas encore terminé de comprendre comment mon cerveau fonctionne).

1 « J'aime »

Bonsoir,

Pour limiter l’usage de de fixtures, tu peux utiliser le package Factoryboy. Il permet de créer des données de tests à partir de tes modèles de données.

Autre solution est de créer des classes pour organiser des tests. Par exemple par type d’utilisateur. Dans une classe tu vas pouvoir initialiser des données au lancement de chaque méthode.

class TestAdmin:
    @pytest.fixture(autouse=True)
    def setup_method_fixture(self):
        # Ajout de tous les fixtures qui seront utilisés dans les tests
        # Elles seront rechargées à chaque test
        self.admin = create_admin(email="email.com", password="password")
    
    def test_admin_is_created(self):
        assert self.admin.email == "email.com"
        assert self.admin.password == "password"
    
    def test_admin_profile_is_created(self):
        assert self.admin.profile is not None
        assert self.admin.profile.admin == self.admin

Tu utilises un framework (Django, FastAPI ou autre) ? Combien de fichiers de tests as-tu ?