Ich bin immer noch in den Lernphasen bezüglich Komponententests und insbesondere im Hinblick auf Spott (ich benutze den PascalMock und DUnit Frameworks). Eine Sache, über die ich jetzt gestolpert bin, war, dass ich keinen Weg gefunden habe, um hartcodierte Implementierungsdetails der getesteten Klasse / Schnittstelle in meinen Komponententest zu finden, und das fühlt sich einfach falsch an ...
Beispiel: Ich möchte eine Klasse testen, die eine sehr einfache Schnittstelle zum Lesen und Schreiben von Anwendungseinstellungen implementiert (im Grunde Name / Wert-Paare). Die Schnittstelle, die dem Verbraucher präsentiert wird, ist völlig unabhängig davon, wo und wie die Werte tatsächlich gespeichert sind (z. B. Registrierung, INI-Datei, XML, Datenbank usw.). Natürlich wird die Zugriffsschicht durch eine andere Klasse implementiert, die in die getestete Klasse beim Aufbau injiziert wird. Ich habe ein Mock-Objekt für diese Zugriffsebene erstellt und bin jetzt in der Lage, die Interface-Implementierungsklasse vollständig zu testen, ohne tatsächlich etwas in eine Registry / INI-Datei / was auch immer zu lesen oder zu schreiben.
Um sicherzustellen, dass sich der Schein genau so verhält wie der reale Fall, wenn die getestete Klasse darauf zugreift, müssen meine Einheitentests das Scheinobjekt einrichten, indem sie explizit erwartete Methodenaufrufe und die von der getesteten Klasse erwarteten Rückgabewerte definieren . Dies bedeutet, dass, wenn ich jemals Änderungen an der Schnittstelle der Zugriffsebene oder an der Art und Weise vornehmen müsste, wie die getestete Klasse diese Schicht verwendet, ich auch die Komponententests für die Klasse ändern muss, die intern diese Schnittstelle verwendet, obwohl < starke> Schnittstelle der Klasse, die ich tatsächlich testen, hat sich überhaupt nicht geändert. Ist das etwas, mit dem ich nur leben muss, wenn ich Mocks benutze oder gibt es eine bessere Möglichkeit, die Klassenabhängigkeiten zu entwerfen, die das vermeiden würden?
Ist das etwas, was ich einfach tun muss? leben mit wenn man Mocks benutzt oder da ist eine bessere Art, das zu entwerfen Klassenabhängigkeiten, die zu vermeiden wären das?
Viele Mocks (besonders sensible Frameworks wie JMock) zwingen Sie dazu, Details zu berücksichtigen, die nicht direkt mit dem Verhalten zu tun haben, das Sie testen möchten. Manchmal kann dies sogar hilfreich sein, indem Sie verdächtigen Code aufdecken zu viel tun und hat zu viele Aufrufe / Abhängigkeiten.
Aber in Ihrem Fall, wenn ich Ihre Beschreibung richtig gelesen habe, klingt es so, als hätten Sie wirklich kein Problem. Wenn Sie den Lese- / Schreib-Layer korrekt und mit einem geeigneten Abstraktionsgrad entwerfen, sollten Sie ihn nicht ändern müssen.
Dies bedeutet, dass wenn ich jemals hätte Änderungen an der Schnittstelle von die Zugriffsschicht oder auf die Art und Weise Die getestete Klasse verwendet diese Schicht I muss auch die Einheit ändern Tests für die Klasse, die intern verwendet diese Schnittstelle, obwohl die Schnittstelle der Klasse, die ich eigentlich bin Testen hat sich überhaupt nicht geändert.
Schreibt man nicht die abstrahierte Zugriffsebene, um dies zu vermeiden? Im Allgemeinen sollte nach dem Open / Closed-Prinzip eine solche Schnittstelle nicht
Um sicherzustellen, dass sich der Mock genau so verhält wie der reale Fall, wenn die getestete Klasse darauf zugreift, müssen meine Komponententests das Mock-Objekt einrichten, indem sie explizit erwartete Methodenaufrufe und die von der getesteten Klasse erwarteten Rückgabewerte definieren.
Korrigieren.
Änderungen an der Schnittstelle der Zugriffsschicht oder an der Art und Weise, wie die getestete Klasse diese Schicht verwendet, muss ich auch die Komponententests ändern
Korrigieren.
obwohl die Schnittstelle der Klasse, die ich gerade teste, sich überhaupt nicht geändert hat.
"Tatsächlich testen"? Du meinst die exponierte Interface-Klasse? Das ist gut.
Die Art und Weise, in der die getestete Klasse (Schnittstelle) die Zugriffsschicht verwendet, bedeutet, dass Sie die interne Schnittstelle in die Zugriffsebene geändert haben. Schnittstellenänderungen (auch interne) erfordern Teständerungen und können zum Bruch führen, wenn Sie etwas falsch gemacht haben.
Damit ist nichts verkehrt. In der Tat ist der springende Punkt, dass jede Änderung an der Zugriffsebene muss Änderungen an den Mocks erfordern, um sicherzustellen, dass die Änderung "funktioniert".
Testen soll nicht "robust" sein. Es soll brüchig sein. Wenn Sie eine Änderung vornehmen, die das interne Verhalten ändert, können Dinge brechen . Wenn deine Tests zu robust wären, würden sie nichts testen - sie würden einfach funktionieren. Und das ist falsch.
Tests sollten nur für den genau richtigen Grund funktionieren.
Nur um ein paar Namen in Ihr Beispiel zu bringen,
Sie sind derzeit daran interessiert, RegistryBasedDictionary zu testen. Die Komponententests würden eine Scheinabhängigkeit für die RegistryAccessor-Rolle einfügen und die erwartete Interaktion mit den Abhängigkeiten testen.
Der einzige Grund, RegistryBasedDictionary-Tests zu ändern, wäre eine Änderung im Verhalten von RegistryBasedDictionary und nicht in einer seiner Abhängigkeiten. Wenn sich also die Interaktion mit ihren Abhängigkeiten oder den Rollen / Verträgen ändert, müssten die Tests aktualisiert werden . Das ist der Preis interaktionsbasierter Tests, die Sie bezahlen müssen, um flexibel zu testen. In der Praxis passiert dies jedoch nicht oft.
Tags und Links unit-testing oop mocking dunit pascalmock