Parfois, les fonctions privées sont simplement des unités fonctionnelles internes à extraire. Alors pourquoi ne pas les tester?

9

Parfois, les fonctions privées d'un module ou d'une classe sont simplement des unités de fonctionnalité internes à extraire, qui pourraient mériter leurs propres tests. Alors pourquoi ne pas les tester? Nous allons écrire des tests pour eux plus tard si / quand ils sont extraits. Alors pourquoi ne pas écrire les tests maintenant, alors qu'ils font toujours partie du même fichier?

Démontrer:

entrez la description de l'image ici

J'ai d'abord écrit module_a. Maintenant, je veux écrire des tests pour cela. Je voudrais tester la fonction "privée" _private_func. Je ne comprends pas pourquoi je n'écrirais pas de test pour cela, si plus tard je pouvais le refactoriser dans son propre module interne de toute façon, puis écrire des tests pour cela.


Supposons que j'ai un module avec les fonctions suivantes (il pourrait également s'agir d'une classe):

def public_func(a):
    b = _do_stuff(a)
    return _do_more_stuff(b)

_do_stuffet _do_more_stuffsont des fonctions «privées» du module.

Je comprends l'idée que nous devrions uniquement tester l'interface publique, pas les détails de mise en œuvre. Cependant, voici la chose:

_do_stuffet _do_more_stuffcontiennent la majorité des fonctionnalités du module. Chacun d'eux pourrait être une fonction publique d'un module «interne» différent. Mais ils ne sont pas encore évolués et suffisamment volumineux pour être extraits dans des fichiers séparés.

Il est donc judicieux de tester ces fonctions, car ce sont des unités de fonctionnalité importantes. S'ils étaient dans différents modules en tant que fonctions publiques, nous les aurions testés. Alors pourquoi ne pas les tester lorsqu'ils ne sont pas encore (ou jamais) extraits dans un fichier différent?

Aviv Cohn
la source
2
"Les méthodes privées sont bénéfiques pour les tests unitaires ..." "... le maintien de la méthode privée m'apporte une amélioration utile et fiable des tests unitaires. En revanche, l'affaiblissement des limitations d'accès" pour la testabilité "ne me donne qu'un obscur, difficile à comprendre morceau de code de test, qui est en outre à risque permanent d'être cassé par une refactorisation mineure; franchement, ce que je reçois ressemble étrangement à une dette technique "
moucheron
3
"Dois-je tester des fonctions privées?" Non, jamais, jamais.
David Arno
2
@DavidArno Pourquoi? Quelle est l'alternative aux tests internes? Seuls les tests d'intégration? Ou rendre plus de choses publiques? (bien que d'après mon expérience, je teste principalement des méthodes publiques sur des classes internes, pas des méthodes privées)
CodesInChaos
1
Si c'est suffisamment important pour qu'il soit nécessaire d'écrire des tests pour cela, alors il devrait déjà être dans un module séparé. Si ce n'est pas le cas, vous testez son comportement à l'aide de l'API publique.
Vincent Savard

Réponses:

14

Le besoin de tester n'est pas le même que le besoin d'être public.

Le code non trivial doit être testé indépendamment de l'exposition. Le comportement non public n'a pas besoin d'exister et encore moins d'être testé.

Ces vues contradictoires peuvent vous conduire à vouloir rendre chaque fonction publique ou à refuser de prendre en compte le code dans une fonction à moins qu'elle ne soit publique.

Ce n'est pas la réponse. Soyez prêt à créer des fonctions d'assistance privées. Testez-les via l'interface publique qui l'utilise le plus possible.

Si le test via l'interface publique n'exerce pas suffisamment la fonction privée, la fonction privée essaie de permettre beaucoup.

La validation peut aider à restreindre ce que permet la fonction privée. Si vous ne pouvez pas passer un null en passant par l'interface publique, vous pouvez toujours lever une exception si une passe de toute façon.

Pourquoi devrais-tu? Pourquoi tester ce que vous ne verrez jamais? Parce que les choses changent. Il peut être privé maintenant mais public plus tard. Le code d'appel pourrait changer. Le code qui rejette explicitement null rend l'utilisation correcte et l'état attendu clairs.

Bien sûr, null pourrait être bien. Ce n'est qu'un exemple ici. Mais si vous attendez quelque chose, il est utile de clarifier cette attente.

Ce n'est peut-être pas le genre de test que vous aviez en tête, mais j'espère que vous serez prêt à créer des fonctions d'assistance privées le cas échéant.

L'envie de tester est bonne mais ne doit pas être le moteur de la conception de votre API publique. Concevez l'API publique pour qu'elle soit facile à utiliser. Ce ne sera probablement pas le cas si toutes les fonctions sont publiques. L'API doit être quelque chose que les gens peuvent comprendre comment utiliser sans plonger dans le code. Ne laissez pas ces personnes se demander à quoi sert cette fonction d'aide étrange.

Masquer les fonctions d'assistance publique dans un module interne est une tentative de respecter la nécessité d'une API propre tout en exposant les assistants pour les tests. Je ne dirai pas que c'est faux. Vous pourriez faire le premier pas vers une couche architecturale différente. Mais s'il vous plaît, maîtrisez l'art de tester les fonctions d'assistance privées à travers les fonctions publiques qui les utilisent en premier. De cette façon, vous n'utiliserez pas trop cette solution de contournement.

candied_orange
la source
J'ai trouvé une approche, j'aimerais avoir votre avis: chaque fois que je suis dans une situation où je veux tester une fonction privée, je vérifie si je peux la tester suffisamment via l'une des fonctions publiques (y compris tous les cas de bord, etc.). Si je le peux, je n'écrirai pas de test pour cette fonction en particulier, mais je ne le testerai qu'en testant les fonctions publiques qui l'utilisent. Cependant, si je pense que la fonction ne peut pas être testée suffisamment via l'interface publique et mérite un test de son propre chef, je vais l'extraire dans un module interne où son public et écrire des tests pour elle. Qu'est-ce que tu penses?
Aviv Cohn
Je dirai que je demande la même chose aux autres gars qui ont répondu ici :) J'aimerais entendre l'opinion de tout le monde.
Aviv Cohn
Encore une fois, je ne vous dirai pas non. Je crains que vous n'ayez rien dit au sujet de la façon dont cela affecte la convivialité. La différence entre public et privé n'est pas structurelle. C'est l'usage. Si la différence entre le public et le privé est une porte d'entrée et une porte arrière, votre travail consiste à construire un hangar dans la cour arrière. Bien. Tant que les gens ne s'y perdent pas.
candied_orange
1
A voté pour "Si le test via l'interface publique n'exerce pas suffisamment la fonction privée, la fonction privée essaie de permettre beaucoup."
Kris Welsh
7

Réponse courte: Non

Réponse plus longue: oui, mais via l'API publique de votre classe

L'idée générale des membres privés d'une classe est qu'ils représentent des fonctionnalités qui devraient être invisibles en dehors de l '«unité» de code, quelle que soit la taille de votre choix. Dans le code orienté objet, cette unité finit souvent par être une classe.

Votre classe doit être conçue de telle sorte qu'il soit possible d'invoquer toutes les fonctionnalités privées via différentes combinaisons d'état d'entrée. Si vous trouvez qu'il n'y a pas de façon relativement simple de le faire, il est probable que votre conception nécessite une attention particulière.


Après clarification de la question, ce n'est qu'une question de sémantique. Si le code en question peut fonctionner comme une unité autonome distincte et est testé comme s'il s'agissait d'un code public, je ne vois aucun avantage à ne pas le déplacer dans un module autonome. À l'heure actuelle, cela ne sert qu'à confondre les futurs développeurs (y compris vous-même, dans 6 mois), à savoir pourquoi le code apparemment public est caché dans un autre module.

richzilla
la source
Salut, merci pour votre réponse :) Veuillez relire la question, j'ai édité pour clarifier.
Aviv Cohn
J'ai trouvé une approche, j'aimerais avoir votre avis: chaque fois que je suis dans une situation où je veux tester une fonction privée, je vérifie si je peux la tester suffisamment via l'une des fonctions publiques (y compris tous les cas de bord, etc.). Si je le peux, je n'écrirai pas de test pour cette fonction en particulier, mais je ne le testerai qu'en testant les fonctions publiques qui l'utilisent. Cependant, si je pense que la fonction ne peut pas être testée suffisamment via l'interface publique et mérite un test de son propre chef, je vais l'extraire dans un module interne où son public et écrire des tests pour elle. Qu'est-ce que tu penses?
Aviv Cohn
Je dirai que je demande la même chose aux autres gars qui ont répondu ici :) J'aimerais entendre l'opinion de tout le monde.
Aviv Cohn
5

L'intérêt des fonctions privées est qu'elles sont des détails d'implémentation cachés qui peuvent être modifiés à volonté, sans changer l'API publique. Pour votre exemple de code:

def public_func(a):
    b = _do_stuff(a)
    return _do_more_stuff(b)

Si vous avez une série de tests qui n'utilisent que public_func, alors si vous le réécrivez à:

def public_func(a):
    b = _do_all_the_new_stuff(a)
    return _do_all_the_old_stuff(b)

alors, tant que le résultat de retour pour une valeur particulière de areste le même, alors tous vos tests seront bons. Si le résultat de retour change, un test échouera, soulignant le fait que quelque chose s'est cassé.

C'est une bonne chose: API publique statique; fonctionnement interne bien encapsulé; et des tests robustes.

Cependant, si vous aviez écrit des tests pour _do_stuffou _do_more_stuffpuis effectué la modification ci-dessus, vous auriez maintenant un tas de tests cassés, non pas parce que la fonctionnalité a changé, mais parce que l'implémentation de cette fonctionnalité a changé. Ces tests devraient être réécrits pour fonctionner avec les nouvelles fonctions, mais après les avoir fait fonctionner, tout ce que vous savez, c'est que ces tests ont fonctionné avec les nouvelles fonctions. Vous auriez perdu les tests d'origine et ne sauriez donc pas si le comportement de public_funca changé et vos tests seraient donc inutiles.

C'est une mauvaise chose: une API changeante; fonctionnement interne exposé étroitement couplé aux tests; et des tests fragiles qui changent dès que des modifications sont apportées à l'implémentation.

Donc non, ne testez pas les fonctions privées. Déjà.

David Arno
la source
Bonjour David, merci pour ta réponse. Veuillez relire ma question, j'ai édité pour clarifier.
Aviv Cohn
Meh. Il n'y a rien de mal à tester les fonctions internes. Personnellement, je n'écris aucune fonction non triviale, à moins que je puisse la couvrir avec quelques tests unitaires, donc interdire les tests sur les fonctions internes m'empêcherait à peu près d'écrire des fonctions internes, me privant d'une technique très utile. Les API peuvent changer et changent; quand ils le font, vous devez modifier les tests. La refactorisation d'une fonction interne (et de son test) ne rompt pas les tests de la fonction externe; c'est tout l'intérêt d'avoir ces tests. Mauvais conseil dans l'ensemble.
Robert Harvey
Ce que vous soutenez vraiment, c'est que vous ne devriez pas avoir de fonctions privées.
Robert Harvey
1
@AvivCohn Ils sont soit assez grands pour justifier des tests, auquel cas ils sont assez grands pour obtenir leur propre fichier; ou ils sont suffisamment petits pour que vous n'ayez pas besoin de les tester séparément. Alors c'est quoi?
Doval
3
@RobertHarvey Non, cela fait que l'argument "décompose les grandes classes en composants faiblement couplés si nécessaire". S'ils ne sont vraiment pas publics, c'est probablement un bon cas d'utilisation pour la visibilité du package privé.
Doval