Dans TDD, devrais-je avoir à écrire Test d'abord ou Interface d'abord?

23

J'apprends TDD en utilisant c #, pour autant que je sache, le test devrait conduire le développement , c'est-à-dire d' abord écrire un test qui échoue après avoir écrit le code minimum pour passer le test, puis refactoriser.

Mais il est également dit que " Programmer vers l'interface, pas la mise en œuvre ", alors écrivez d' abord une interface . C'est là que ma confusion commence, si j'écris d'abord Interface, cela viole deux choses

  1. Le code écrit pour l'interface n'est pas piloté par test .

  2. Ce n'est pas le strict minimum, je peux évidemment l'écrire avec une classe simple.

Dois-je également commencer par écrire des tests d'interface? sans implémentation, que vais-je tester?

Si cette question semble stupide, désolé pour cela, mais je suis complètement confus. Peut-être que je prends les choses trop à la lettre.

k4vin
la source
8
«Programmer vers une interface» signifie séparer ce dont vous avez besoin d'un morceau de code de la façon dont cela est fait. Cela ne signifie pas d'utiliser littéralement un interfacepour tout. A classfournit également une interface, car vous pouvez masquer les détails d'implémentation dans des privatevariables.
Doval
@Doval, oui, vous n'avez pas besoin d'une interface pour tout, juste ce qu'on appelle un contract. Cela pourrait être sous la forme d'une classe abstraite, par exemple, bien qu'il ne devrait pas s'agir d'une classe / méthode virtuelle car vous ne devriez pas pouvoir l'instancier.
trysis
2
TDD dit: «écrivez un test qui échoue». Certains TDDers stricts disent qu'il est considéré comme un "échec" si vous ne pouvez pas compiler le test car le type de données sur lequel il fonctionne n'a pas encore été déclaré.
Solomon Slow

Réponses:

29

Votre première violation («Le code qui est écrit pour l'interface n'est pas pilotée par le test.») N'est pas valide. Prenons un exemple trivial. Supposons que vous écrivez une classe de calculatrice et que vous écrivez une opération d'addition. Quel test pourriez-vous passer?

public class CalculatorTest {
    @Test
    public void testAddTwoIntegers() {
        Calculator calc = new Calculator();
        int result = calc.add(2, 2)
        Assert.assertEquals(4, result);
    }
}

Votre test vient de définir l'interface. C'est la addméthode, tu vois? addprend deux arguments et retourne leur somme. Vous pourrez déterminer ultérieurement que vous avez besoin de plusieurs calculatrices et extraire une interface Java (dans ce cas) à ce moment-là. Vos tests ne devraient pas changer alors, puisque vous avez testé l' interface publique de cette classe.

À un niveau plus théorique, les tests sont la spécification exécutable d'un système. Les interfaces avec un système doivent être pilotées par les utilisateurs de ce système et les tests sont la première méthode dont vous disposez pour définir les interactions.

Je ne pense pas que vous puissiez séparer la conception d'interface de la conception de test. Définir les interactions et concevoir des tests pour elles sont la même opération mentale - lorsque j'envoie ces informations dans une interface, j'attends un certain résultat. Quand quelque chose ne va pas avec mon entrée, je m'attends à cette erreur. Vous pouvez faire ce travail de conception sur papier, puis écrire vos tests à partir de cela, ou vous pouvez les faire en même temps - cela n'a pas vraiment d'importance.

Michael K
la source
2
+1 " Je ne pense pas que vous puissiez séparer la conception d'interface de la conception de test " devrait être en gras, à mon humble avis :)
Binary Worrier
Il est encore plus facile de montrer, si vous voulez tester plus d'une implémentation d'une fonctionnalité, dit une importation XML et une importation CSV, vous pouvez les tester avec exactement la même méthode à partir de la même interface bien que l'implémentation changera. De plus, les tests impliquent souvent quelques moqueries, et pour cette interface sont nécessaires.
Walfrat
Vous dites que le test n'a pas besoin de changer, mais new Calculator()la mise en œuvre est-elle correcte? Si une nouvelle implémentation est nécessaire, vous feriez peut-être alors un MultiplicationCalculator, et vous auriez besoin de changer le test à utiliser new AdditionCalculator()pour qu'il passe encore? Ou est-ce que je manque quelque chose?
Steve Chamaillard
3
@SteveChamaillard Bien sûr, si votre conception vous fait changer le nom de classe de Calculator en AdditionCalculator, le test devra changer pour correspondre. Bien sûr, en faisant TDD, ce qui se passerait réellement, c'est que vous changeriez d'abord le test, et le changement de classe suivrait afin de faire passer le test.
Eric King
5

Que faisons-nous quand nous écrivons un interface? Écrivons-nous du code ou concevons-nous?

Je ne suis pas fan de la notion de Test Driven Design, mais j'aime Test Driven Development . Personnellement, j'ai obtenu mes meilleurs résultats lorsque je conçois la classe à l'avance en concevant l'interface avant d'écrire un test. Je ne compte pas l'interface comme du code. L'interface est une conception que je vais implémenter en utilisant TDD. Il est probable que cela change une évolution au fur et à mesure que je travaille, mais c'est ma feuille de route (avec ma liste de tests).

Je m'arrêterai avant de commencer à déclamer, mais j'espère que c'est une façon utile pour vous d'y penser.

Canard en caoutchouc
la source
4

Dans TDD, devrais-je avoir à écrire Test d'abord ou Interface d'abord?

Tout dépend de la façon dont vous voulez faire du TDD orthodoxe / religieux .

J'apprends le TDD

Puisque vous apprenez, vous devez expérimenter pour obtenir un flux de travail personnel, qui fonctionne pour vous.

  1. Si vous voulez le faire selon les livres , vous écrivez d'abord un test, qui échouera évidemment, car vous commencez sans aucun code. Ensuite, vous écrivez du code pour réussir le test. Si cela est fait, vous êtes libre de refactoriser le code existant, car vous avez un test qui fournit une sorte de filet de sécurité pour les refactorings. Décider d'utiliser une interface est une sorte de refactoring.

  2. Outre TDD ou non: La question de savoir si utiliser une interface ou non n'est pas intéressante en premier lieu. Bien sûr, si vous êtes sûr, vous avez un comportement différent que vous souhaitez répartir sur plusieurs objets, il est logique de penser à utiliser une interface: par exemple, si vous avez une sorte de sortie vers différentes destinations, il est logique de l'implémenter via une interface Writer et ont différentes classes pour la sortie ( FileWriter , Printer etc.). Bien que ce soit un dicton courant d' écrire sur une interface , cela ne signifie pas: utiliser une interface pour tout . Parfois, c'est un niveau d'indirection trop important. Btw. il en va de même pour les services. Mais c'est un sujet différent.

  3. D'un autre côté, vous pouvez développer le test piloté d'une autre manière: concevoir votre code pour la testabilité. Cela signifie que vous écrivez du code, qui est facile à tester - bien que vous écriviez les tests par la suite . Peu importe si vous écrivez des tests avant ou après, tant que vous testez de toute façon.

Thomas Junk
la source
5
Je ne suis pas d'accord avec le dernier point "Peu importe si vous écrivez des tests avant ou après, tant que vous testez quand même". S'il vous arrive de passer le test par la suite, vous ne pouvez pas être sûr que le test teste la bonne chose.
k4vin
4
Comme je l'ai dit ... Cela dépend de votre degré d'orthodoxie ...
Thomas Junk
2

TDD ou BDD signifierait Faire d'abord vos interfaces de domaine, puis écrire des tests par rapport à elles par mon interprétation. L'implémentation d'une interface a un comportement attendu.

c'est toujours un test avant le code car une interface ne contient aucune logique testable, c'est la structure contre laquelle vous écrivez un test.

Je le ferais comme suit

  1. Écrivez le comportement semi-formel (étant donné: quand: alors :)

  2. Écrire l'interface (pour héberger la méthode d'encapsulation du comportement)

  3. Écrivez le test qu'il identifie (entrez le donné, appelez le quand, testez le alors)

  4. Écrire / modifier le béton (classe qui implémente l'interface) pour réussir le test

user3721330
la source
0

N'écrivez jamais de tests avant d'avoir conçu les interfaces. Lorsque vous pensez aux types de tests à écrire (conception de test), vous ne devez pas également concevoir (architecturer) simultanément votre application. Ne pensez pas à deux choses en même temps. Avez-vous entendu parler de la séparation des préoccupations? Cela s'applique non seulement à la structure physique de votre code mais également à votre processus de réflexion.

Décidez d'abord comment votre application doit être conçue. Cela signifie que vous concevez vos interfaces et les relations entre ces interfaces. Jusqu'à ce que vous ayez fait cela, vous ne devriez pas commencer à penser aux tests. Une fois que vous savez quelles sont vos interfaces, vous pouvez soit les créer d'abord, puis écrire des tests contre elles, soit écrire des tests d'abord, puis les créer. Dans ce dernier cas, vous ne pourrez évidemment pas compiler les tests. Je ne vois aucun mal ni aucune violation de la philosophie TDD dans la création des interfaces avant les tests.

Nissim Levy
la source
le raisonnement dans la première réponse semble plus convaincant: «Je ne pense pas que vous puissiez séparer la conception d'interface de la conception de test. Définir les interactions et concevoir des tests pour eux est la même opération mentale - lorsque j'envoie ces informations dans une interface, j'attends un certain résultat . Quand quelque chose ne va pas avec mon entrée, je m'attends à cette erreur ... "
moucher
@gnat Je crois que Nissam fait référence ici au interfacemot clé C # , pas au terme général "interface".
RubberDuck
@RubberDuck Je le pense aussi et je pense que c'est une mauvaise approche. "Jusqu'à ce que vous ayez fait cela, vous ne devriez pas commencer à penser aux tests ..." Comme je l'ai écrit, le raisonnement dans la première réponse semble plus convaincant, à la fois au sens général de l'interface et au sens du mot clé concret
gnat
Assez juste @gnat qui n'était pas clair à partir de votre commentaire. Personnellement, je suis d'accord avec Nissam ici. Je trouve que cela empêche les gens d'utiliser TDD comme excuse pour ne pas concevoir du tout. YMMV.
RubberDuck
-2

C'est correct d'écrire l'interface / le code / le test en même temps tant que leur incorporation dans le projet est atomique.

Sauf si votre patron est religieux à propos de TDD, auquel cas vous devrez probablement écrire une interface vide -> test -> code minimal (étape inutile) -> plus de tests -> plus de code inutile -> plus de tests -> enfin écrire le vrai code - > terminé.

alternative
la source