@maki : Ich bin heute dazu gekommen, das Essay von Fowler zu lesen. Insbesondere der Abschnitt Coupling Tests to Implementations spiegelt genau meine Bedenken bzgl. der Kopplung zwischen Implementierung und Test wider. Obwohl ich in den meisten anderen Bereichen wohl eher den „Mockist“-Weg vorziehe, hatte ich diesbezüglich wohl eher die „klassische“ Denkweise. Das zustandsbasierte Testing passt aber nicht so ganz zum verhaltensorientierten Testing beim mocken.
Man muss natürlic dazu sagen, dass der Artikel etwas älter ist (aber nicht wirklich „veraltet“), er geht auf JMock ein (die alte Version), die felxibler war (nach damaligen Gesichtspunkten):
This can be worsened by the nature of mock toolkits. Often mock tools specify very specific method calls and parameter matches, even when they aren’t relevant to this particular test. One of the aims of the jMock toolkit is to be more flexible in its specification of the expectations to allow expectations to be looser in areas where it doesn’t matter, at the cost of using strings that can make refactoring more tricky.
Denke ihm geht es darum dass die Parameter für die Mock Aufrufe flexbiler gehanghabt werden, um genau zu sein:
Anstatt auch die Werte (Zustände, States) der Mockaufrufe zu prüfen, wird nur geprüft ob die entsprechenden Methoden in der richtigen Reihenfolge aufgerufen wurden (Verhalten/Behaviour).
Das ist natürlich alles etwas länger her (2007) und heute gibt es IMHO eigentlich nur ein Mocking Framework das man nutzen sollte: JMockit
Kann alle dreckigen Tricks (statische Felder mocken, new Operator umbiegen, etc. pp., wichtig bei Legacy Code), aber auch ganz „einfache Dinge“ für normales TDD.
Das Tests beim Refactoring mitgeändert werden müssen ist kar, idealerweise eben für 1 Änderung im Prodcode nur 1 Änderung im testcode.
Wie du bereits sagtest ist DRY extrem wichtig, auch im Testcode.
Stell dir vor du hast 3 Testklassen mit insgesamt ca. 10 Tests, jedes dieser Tests nutzt den Konstruktor von Klasse A direkt. Jetzt änderst du den Konstruktor von Klasse A ab und darfst 3 Klassen/30 Test abändern… die Lösung ist sehr einfach, Factory Methode/Object Mother/Builder, damit muss nur an einer einzigen Stelle in den Tests geändert wenn sich der Konstruktor ändert.
Das ist auch gar nicht so wild, ich ändere zB. einfach so den Konstruktor ab und wenn ich dann sehe dass sehr viel „rot“ wird in den Tests, mache ich das schnell wieder rückgängig und refactore dann die Tests erst.
Ob man seine Tests als Minenfeld oder als Sicherheitsnetz empfindet kommt IME stark darauf an wie man die Tests und den ProdCode schreibt.
Bin ehrlich gesagt die letzten 2 Jahre beruflich auch nicht mehr zu TDD gekommen Legacy Code halt, muss mir wohl ein neues Greenfield Projekt suchen…
Wenn man sich mal daran gewöhnt hat tut es richtig weh ohne, keiner macht beim ersten mal alles richtig, ohne den Refactoring Schritt bleibt der erste Entwurf oft der Finale…
Genau dieses Mantra versuche ich mir einzubläuen (darauf wird ja auch überall hingewiesen, wenn man was zum Thema TDD liest). Aber es ist doch vorerst so viel schneller, „mal eben schnell“ dieses oder jenes Feature ohne den Test vorher geschrieben zu haben zu implementieren
Ja, deine Gewohnheit
Bei TDD kommt oft erst die Frage „Wie würde ich das testen?“ bevor man sich überlegt „Wie würde ich da implementieren?“.
Mein erster Test in einer neuen Testklasse (bevor eine Implementierung/Interface geschrieben ist) lautet in 99% der Fälle
public void first() {...}
und ruft nur den Kontruktor auf um eine Instanz zu erstellen, danach kommt die Sache ins Rollen und dieser first Tests wird sehr schnell gelöscht. Man muss nur mal in Fahrt kommen…
Hier noch ein älter Artikel zum Thema Mocks und TDD: Mock Roles not Objects
Ist nicht Topaktuell aber geht trotzdem auf Dinge wie „Interface Discovery“ ein, ein Punkt der TDD so wertvoll macht weil es damit zum Design Tool wird.