Sollten Unit-Tests nicht meine eigenen Methoden verwenden?

83

Heute habe ich ein " JUnit- Grundlagen" -Video angesehen und der Autor hat gesagt, dass Sie beim Testen einer bestimmten Methode in Ihrem Programm keine anderen Ihrer eigenen Methoden verwenden sollten.

Um genauer zu sein, er sprach über das Testen einer Methode zur Datensatzerstellung, die einen Namen und einen Nachnamen für Argumente verwendete und diese verwendete, um Datensätze in einer bestimmten Tabelle zu erstellen. Er behauptete jedoch, dass er beim Testen dieser Methode nicht seine anderen DAO- Methoden verwenden sollte, um die Datenbank abzufragen und das Endergebnis zu überprüfen (um zu überprüfen, ob der Datensatz tatsächlich mit den richtigen Daten erstellt wurde). Er forderte dazu auf, zusätzlichen JDBC- Code zu schreiben , um die Datenbank abzufragen und das Ergebnis zu überprüfen.

Ich glaube, ich verstehe den Geist seiner Behauptung: Sie möchten nicht, dass der Testfall einer Methode von der Richtigkeit der anderen Methoden (in diesem Fall der DAO-Methode) abhängt, und dies wird erreicht, indem Sie (erneut) Ihre eigene Validierung schreiben / unterstützender Code (der spezifischer und fokussierter sein sollte, daher einfacherer Code).

Nichtsdestotrotz protestierten Stimmen in meinem Kopf mit Argumenten wie Code-Vervielfältigung, unnötigem zusätzlichen Aufwand usw. Ich meine, wenn wir die gesamte Testbatterie betreiben und alle unsere öffentlichen Methoden gründlich testen (in diesem Fall auch die DAO-Methode), sollte das nicht sein Ist es nicht in Ordnung, nur einige dieser Methoden zu verwenden, während Sie andere Methoden testen? Wenn einer von ihnen nicht tut, was er tun soll, schlägt sein eigener Testfall fehl, und wir können ihn beheben und die Testbatterie erneut ausführen. Keine Notwendigkeit für Code-Duplizierung (auch wenn der Code-Duplizierung etwas einfacher ist) oder unnötiger Aufwand.

Ich habe ein starkes Gefühl bei der Sache wegen einiger neuer Excel - VBA - Anwendungen I (richtig stückgeprüft dank geschrieben habe Rubberduck für VBA ), wobei diese Empfehlung Anwendung eine Menge zusätzlicher zusätzliche Arbeit bedeuten würde, ohne wahrgenommen Nutzen.

Können Sie uns bitte Ihre Einsichten dazu mitteilen?

carlossierra
quelle
79
Es ist etwas seltsam, einen Komponententest zu sehen, an dem die Datenbank überhaupt beteiligt ist
Richard Tingle,
4
IMO ist es in Ordnung, andere Klassen zu nennen, wenn sie schnell genug sind. Verspotten Sie alles, was auf die Festplatte oder über ein Netzwerk gehen muss. Es hat keinen Sinn, eine einfache alte IMO zu verspotten.
RubberDuck
2
Hast du einen Link zu diesem Video?
candied_orange
17
" Richtig Unit-getestet dank Rubberduck für VBA " - Sie, mein Herr, haben gerade meinen Tag gemacht. Ich würde den Tippfehler korrigieren und ihn von "RubberDuck" in "Rubberduck" bearbeiten, aber ich hätte das Gefühl, dass ein Spammer dies tut und einen Link zu rubberduckvba.com hinzufügt (mir gehört der Domainname und dem gehört das Projekt mit) @RubberDuck) - also werde ich hier stattdessen nur einen Kommentar abgeben. Jedenfalls ist es großartig zu sehen, wie Leute das Tool benutzen, das in den letzten zwei Jahren für die meisten meiner schlaflosen Nächte verantwortlich war! =)
Mathieu Guindon
4
@ Mat'sMug und RubberDuck Ich liebe, was du mit RubberDuck machst. Mach bitte weiter so. Sicherlich ist mein Leben dadurch einfacher (ich mache eine Menge kleiner Programme und Prototypen in Excel VBA). Übrigens, die Rubberduck-Erwähnung in meinem Beitrag war nur, dass ich versucht habe, nett zu RubberDuck selbst zu sein, was wiederum nett zu mir hier in PE war. :)
carlossierra

Antworten:

186

Der Geist seiner Behauptung ist in der Tat richtig. Der Zweck von Komponententests besteht darin, Code zu isolieren und unabhängig von Abhängigkeiten zu testen, damit fehlerhafte Verhaltensweisen schnell erkannt werden können, wo sie auftreten.

Vor diesem Hintergrund ist Unit Testing ein Werkzeug, das Ihren Zwecken dienen soll. Es ist kein Altar, zu dem man beten muss. Manchmal bedeutet dies, Abhängigkeiten zu belassen, weil sie zuverlässig genug funktionieren und Sie sich nicht die Mühe machen wollen, sie zu verspotten. Manchmal bedeutet dies, dass einige Ihrer Komponententests tatsächlich ziemlich eng sind, wenn nicht sogar Integrationstests.

Letztendlich werden Sie nicht darauf eingestuft, wichtig ist das Endprodukt der getesteten Software, aber Sie müssen nur darauf achten, wann Sie die Regeln biegen und entscheiden, wann sich die Kompromisse lohnen.

Whatsisname
quelle
117
Ein Hoch auf Pragmatismus.
Robert Harvey
6
Ich würde hinzufügen, dass dies nicht unbedingt die Zuverlässigkeit ist, da die Geschwindigkeit das Problem ist, das zum Herausfiltern von Abhängigkeiten führt. Das Mantra des Einzeltests ist jedoch so überzeugend, dass dieses Argument oft übersehen wird.
Michael Durrant
4
"... Unit Testing ist ein Werkzeug, und es soll Ihren Zwecken dienen. Es ist kein Altar, zu dem man beten muss." - das!
Wayne Conrad
15
"Kein Altar, zu dem man beten muss" - wütende TDD-Trainer kommen!
Den
5
@ jpmc26: Schlimmer noch, Sie möchten nicht, dass alle Unit-Tests bestanden werden, da sie den Web-Service korrekt gehandhabt haben, was ein gültiges und korrektes Verhalten ist, aber niemals den Code ausüben, wenn er aktiv ist! Sobald Sie es stumm geschaltet haben, können Sie wählen, ob es "hoch" oder "runter" ist, und beide testen.
Steve Jessop
36

Ich denke, das hängt von der Terminologie ab. Für viele ist ein "Komponententest" eine sehr spezifische Sache, und per Definition kann kein Bestehen / Nichtbestehen-Zustand vorliegen, der von einem Code außerhalb der zu testenden Einheit (Methode, Funktion usw.) abhängt . Dies würde die Interaktion mit einer Datenbank einschließen.

Für andere ist der Begriff "Komponententest" viel lockerer und umfasst jede Art von automatisiertem Testen, einschließlich Testcode, der integrierte Teile der Anwendung testet.

Ein Testpurist (wenn ich diesen Begriff verwenden darf) könnte dies einen Integrationstest nennen , der sich von einem Komponententest dadurch unterscheidet, dass der Test von mehr als einer reinen Codeeinheit abhängt .

Ich vermute, dass Sie die lockerere Version des Begriffs "Komponententest" verwenden und sich tatsächlich auf einen "Integrationstest" beziehen.

Wenn Sie diese Definitionen verwenden, sollten Sie sich bei einem "Komponententest" nicht auf Code außerhalb des Prüflings verlassen. Bei einem "Integrationstest" ist es jedoch durchaus sinnvoll, einen Test zu schreiben, der einen bestimmten Code ausübt und dann die Datenbank auf die Bestehenskriterien überprüft.

Ob Sie sich auf Integrationstests oder Unit-Tests oder beides verlassen sollten, ist ein viel größeres Thema.

Eric King
quelle
Sie haben Recht mit Ihrem Verdacht. Ich verwende die Begriffe falsch und kann jetzt sehen, wie jeder Testtyp angemessener gehandhabt werden kann. Vielen Dank!
Carlossierra
11
"Für andere ist der Begriff" Komponententest "viel lockerer" - abgesehen von allem anderen sind sogenannte "Komponententest-Frameworks" eine sehr bequeme Möglichkeit, übergeordnete Tests zu organisieren und durchzuführen ;-)
Steve Jessop,
1
Anstatt einer "reinen" vs "losen" Unterscheidung, die mit Konnotationen beladen ist, würde ich vorschlagen, dass die Unterscheidung zwischen denen besteht, die "Einheiten", "Abhängigkeiten" usw. aus einer Domain-Perspektive betrachten (z. B. "Authentifizierung" würde zählen) als eine Einheit würde "Datenbank" als eine Abhängigkeit usw. zählen) im Vergleich zu denen, die sie aus einer Programmiersprachenperspektive betrachten (z. B. Methoden sind Einheiten, Klassen sind Abhängigkeiten). Wenn ich definiere, was unter Phrasen wie "Prüfling" zu verstehen ist, können Argumente ("Sie liegen falsch!") In Diskussionen umgewandelt werden ("Ist dies die richtige Granularität?").
Warbo
1
@EricKing Natürlich ist für die große Mehrheit derjenigen in Ihrer ersten Kategorie die Idee , die Datenbank überhaupt in Komponententests einzubeziehen, ein Gräuel, egal ob Sie Ihre DAOs verwenden oder direkt auf die Datenbank zugreifen.
Periata Breatta
1
@AmaniKilumanga Die Frage lautete im Grunde: "Jemand sagt, ich sollte dieses Ding nicht in Einheitentests machen, aber ich mache es in Einheitentests und ich denke, es ist in Ordnung. Ist das in Ordnung?" Und meine Antwort war "Ja, aber die meisten Leute würden das einen Integrationstest anstelle eines Komponententests nennen." Was Ihre Frage betrifft, weiß ich nicht, was Sie unter "Können Integrationstests Produktionscodemethoden verwenden?" Verstehen.
Eric King
7

Die Antwort ist ja und nein ...

Einzigartige Tests, die isoliert durchgeführt werden, sind ein absolutes Muss, da Sie damit die Grundlagen dafür schaffen können, dass Ihr Low-Level-Code ordnungsgemäß funktioniert. Beim Codieren größerer Bibliotheken finden Sie jedoch auch Codebereiche, die Tests erfordern, die sich über mehrere Einheiten erstrecken.

Diese einheitenübergreifenden Tests sind gut für die Codeabdeckung und beim Testen der End-to-End-Funktionalität geeignet. Sie weisen jedoch einige Nachteile auf, die Sie berücksichtigen sollten:

  • Ohne einen isolierten Test zur Bestätigung der Fehlerursache erfordert ein fehlgeschlagener "Cross-Unit" -Test eine zusätzliche Fehlerbehebung, um festzustellen, was mit Ihrem Code nicht stimmt
  • Wenn Sie sich zu sehr auf Unit-übergreifende Tests verlassen, können Sie aus der vertraglichen Denkweise herauskommen, in der Sie beim Schreiben von SOLID Object-Oriented Code immer sein sollten. Isolierte Tests sind in der Regel sinnvoll, da Ihre Einheiten nur eine grundlegende Aktion ausführen sollten.
  • End-to-End-Tests sind wünschenswert, können jedoch gefährlich sein, wenn Sie für einen Test in eine Datenbank schreiben oder eine Aktion ausführen müssen, die Sie in einer Produktionsumgebung nicht ausführen möchten. Dies ist einer der vielen Gründe, warum Mocking-Frameworks wie Mockito so beliebt sind, weil es Ihnen ermöglicht, ein Objekt zu fälschen und einen End-to-End-Test nachzuahmen, ohne tatsächlich etwas zu ändern, das Sie nicht sollten.

Am Ende des Tages möchten Sie beides haben. Viele Tests, die Funktionen auf niedriger Ebene erfassen, und einige, die die End-to-End-Funktionalität testen. Konzentrieren Sie sich zunächst auf den Grund, warum Sie Tests schreiben. Sie sollen Ihnen die Gewissheit geben, dass Ihr Code die erwartete Leistung erbringt. Was auch immer Sie tun müssen, um dies zu erreichen, ist in Ordnung.

Die traurige, aber wahre Tatsache ist, dass Sie, wenn Sie automatisierte Tests verwenden, bereits eine Menge Entwickler hinter sich haben. Gute Testpraktiken sind das erste, was ein Entwicklerteam mit schwierigen Fristen konfrontiert. Solange Sie sich also an Ihre Waffen halten und Unit-Tests schreiben, die einen aussagekräftigen Eindruck davon vermitteln, wie Ihr Code ablaufen soll, wäre ich nicht besessen davon, wie "rein" Ihre Tests sind.

DanK
quelle
4

Ein Problem bei der Verwendung anderer Objektmethoden zum Testen einer Bedingung besteht darin, dass Sie Fehler verpassen, die sich gegenseitig aufheben. Noch wichtiger ist, dass Sie den Schmerz einer schwierigen Testimplementierung verpassen und dass Ihnen dieser Schmerz etwas über Ihren zugrunde liegenden Code beibringt. Schmerzhafte Tests bedeuten jetzt später schmerzhafte Wartung.

Verkleinern Sie Ihre Einheiten, teilen Sie Ihre Klasse, refaktorieren Sie sie und gestalten Sie sie neu, bis es einfacher ist, zu testen, ohne den Rest Ihres Objekts erneut zu implementieren. Holen Sie sich Hilfe, wenn Sie der Meinung sind, dass Ihr Code oder Ihre Tests nicht weiter vereinfacht werden können. Denken Sie an einen Kollegen, der anscheinend immer Glück hat und Aufträge erhält, die sich einfach und sauber testen lassen. Bitten Sie ihn oder sie um Hilfe, weil es kein Glück ist.

Karl Bielefeldt
quelle
1
Schlüssel hier ist in Einheit. Wenn Sie andere Verfahren und Prozesse anrufen müssen, ist dies für mich keine Einheit. Wenn mehrere Aufrufe getätigt werden und mehrere Schritte validiert werden müssen, zerlegen Sie einen großen Code als eine Einheit in kleinere Teile, die einen einzelnen Teil der Logik abdecken. Normalerweise würde auch ein DB-Aufruf verspottet oder ein im Speicher befindlicher DB im Komponententest verwendet, um auf einen echten DB zuzugreifen und Daten nach dem Test rückgängig zu machen.
DLB
1

Wenn die zu testende Funktionseinheit lautet: "Werden die Daten dauerhaft gespeichert und sind sie abrufbar?", Müsste ich den Komponententest durchführen: "In einer realen Datenbank speichern, alle Objekte zerstören, die Verweise auf die Datenbank enthalten, und dann den Code aufrufen, um die abzurufen." Objekte zurück.

Das Testen, ob ein Datensatz in der Datenbank erstellt wird, scheint eher mit den Implementierungsdetails des Speichermechanismus zu tun zu haben, als die Funktionseinheit zu testen, die für den Rest des Systems verfügbar ist.

Vielleicht möchten Sie den Test verkürzen, um die Leistung mithilfe einer nachgebildeten Datenbank zu verbessern, aber ich habe Auftragnehmer gehabt, die genau das getan haben und am Ende ihres Vertrags mit einem System abgereist sind, das solche Tests bestanden hat, aber tatsächlich nichts in der Datenbank gespeichert hat zwischen Systemneustarts.

Sie können darüber streiten, ob "Einheit" im Komponententest eine "Codeeinheit" oder eine "Funktionseinheit" ist. Diese Funktion wird möglicherweise von vielen Codeeinheiten zusammen erstellt. Ich halte das nicht für eine sinnvolle Unterscheidung - die Fragen, an die ich denken möchte, lauten: "Sagt der Test etwas darüber aus, ob das System geschäftlichen Nutzen bringt?" Und "Ist der Test spröde, wenn sich die Implementierung ändert?" Tests wie der beschriebene sind nützlich, wenn Sie das System testen möchten - Sie haben noch nicht das 'Objekt aus Datenbankdatensatz abrufen' geschrieben, können also nicht die gesamte Funktionalität testen -, aber sie sind spröde gegenüber Implementierungsänderungen, also würde ich Entfernen Sie sie, sobald der vollständige Betrieb überprüft werden kann.

Pete Kirkham
quelle
1

Der Geist ist richtig.

Idealerweise testen Sie in einem Unit-Test eine Unit (eine einzelne Methode oder eine kleine Klasse).

Im Idealfall würden Sie das gesamte Datenbanksystem auslagern. Dh, Sie würden Ihre Methode in einer gefälschten Umgebung ausführen und nur sicherstellen, dass die richtigen DB-APIs in der richtigen Reihenfolge aufgerufen werden. Sie möchten die Datenbank ausdrücklich nicht testen, wenn Sie eine Ihrer eigenen Methoden testen.

Vorteile gibt es viele. Vor allem werden Tests schnell blind, da Sie sich nicht darum kümmern müssen, eine korrekte DB-Umgebung einzurichten und diese anschließend zurückzusetzen.

Natürlich ist dies ein hohes Ziel in jedem Softwareprojekt, das dies nicht von Anfang an richtig macht. Aber es ist der Geist der Einheitentests .

Beachten Sie, dass es andere Tests wie Funktionstests, Verhaltenstests usw. gibt, die von diesem Ansatz abweichen. Verwechseln Sie "Testen" nicht mit "Komponententests".

AnoE
quelle
"Stellen Sie einfach sicher, dass die richtigen DB-APIs in der richtigen Reihenfolge aufgerufen werden" - ja, sicher. Bis sich herausstellt, dass Sie die Dokumentation falsch gelesen haben und die tatsächliche richtige Reihenfolge, die Sie hätten verwenden sollen, völlig anders ist. Verspotten Sie keine Systeme außerhalb Ihrer Kontrolle.
Joker_vD
4
@Joker_vD Dafür sind Integrationstests gedacht. In Unit-Tests sollten externe Systeme unbedingt verspottet werden.
Ben Aaronson
1

Es gibt mindestens einen sehr wichtigen Grund , den direkten Datenbankzugriff in Ihren Komponententests für Geschäftscode, der mit der Datenbank interagiert, nicht zu verwenden: Wenn Sie Ihre Datenbankimplementierung ändern, müssen Sie alle diese Komponententests neu schreiben. Benennen Sie eine Spalte um. Für Ihren Code müssen Sie nur eine einzelne Zeile in Ihrer Data-Mapper-Definition ändern. Wenn Sie Ihren Data Mapper beim Testen jedoch nicht verwenden, müssen Sie auch jeden einzelnen Komponententest ändern , der auf diese Spalte verweist . Dies könnte zu einem außerordentlichen Arbeitsaufwand führen, insbesondere bei komplexeren Änderungen, die sich weniger gut suchen und ersetzen lassen.

Wenn Sie Ihren Data Mapper als abstrakten Mechanismus verwenden, anstatt direkt mit der Datenbank zu kommunizieren, können Sie die Abhängigkeit von der Datenbank leichter vollständig entfernen , was derzeit möglicherweise nicht relevant ist, aber wenn Sie bis zu Tausenden von Komponententests und allen davon erhalten Wenn Sie auf eine Datenbank zugreifen, sind Sie dankbar, dass Sie diese leicht überarbeiten können, um diese Abhängigkeit zu beseitigen, da Ihre Testsuite von Minuten auf Sekunden sinkt, was einen enormen Vorteil für Ihre Produktivität haben kann.

Ihre Frage zeigt an, dass Sie in die richtige Richtung denken. Wie Sie vermuten, sind Unit-Tests auch Code. Es ist genauso wichtig, dass sie wartbar sind und sich leicht an zukünftige Änderungen anpassen lassen, wie für den Rest Ihrer Codebasis. Bemühen Sie sich, sie lesbar zu halten und Duplikate zu vermeiden, und Sie werden eine viel bessere Testsuite dafür haben. Und eine gute Testsuite wird verwendet. Und eine Test-Suite, die verwendet wird, hilft, Fehler zu finden, während eine, die nicht verwendet wird, nur wertlos ist.

Periata Breatta
quelle
1

Die beste Lektion, die ich beim Erlernen von Unit-Tests und Integrationstests gelernt habe, ist, keine Methoden zu testen, sondern Verhaltensweisen zu testen. Mit anderen Worten, was macht dieses Objekt?

Wenn ich das so betrachte, werden eine Methode, die Daten beibehält, und eine andere Methode, die sie zurückliest, testbar. Wenn Sie die Methoden speziell testen, erhalten Sie am Ende einen Test für jede einzelne Methode, z. B .:

@Test
public void canSaveData() {
    writeDataToDatabase();
    // what can you assert - the only expectation you can have here is that an exception was not thrown.
}

@Test
public void canReadData() {
    // how do I even get data in there to read if I cannot call the method which writes it?
}

Dieses Problem tritt aufgrund der Perspektive der Testmethoden auf. Teste keine Methoden. Verhalten testen. Wie verhält sich die WidgetDao-Klasse? Widgets bleiben erhalten. Ok, wie verifizierst du, dass Widgets bestehen bleiben? Nun, was ist die Definition von Persistenz? Es bedeutet, wenn Sie es schreiben, können Sie es wieder zurücklesen. Lesen + Schreiben zusammen wird also zu einem Test und meiner Meinung nach zu einem aussagekräftigeren Test.

@Test
public void widgetsCanBeStored() {
    Widget widget = new Widget();
    widget.setXXX.....
    // blah

    widgetDao.storeWidget(widget);
    Widget stored = widgetDao.getWidget(widget.getWidgetId());
    assertEquals(widget, stored);
}

Das ist ein logischer, zusammenhängender, zuverlässiger und meiner Meinung nach aussagekräftiger Test.

Die anderen Antworten konzentrieren sich darauf, wie wichtig Isolation ist, wie pragmatisch und realistisch eine Debatte ist und ob ein Komponententest eine Datenbank abfragen kann oder nicht. Diese beantworten die Frage allerdings nicht wirklich.

Um zu testen, ob etwas gespeichert werden kann, müssen Sie es speichern und dann zurücklesen. Sie können nicht testen, ob etwas gespeichert ist, wenn Sie es nicht lesen dürfen. Testen Sie das Speichern von Daten nicht getrennt vom Abrufen von Daten. Sie werden mit Tests enden, die Ihnen nichts sagen.

Brandon
quelle
Sehr aufschlussreiche Herangehensweise, und wie Sie sagen, ich denke, Sie haben wirklich einen der Hauptzweifel getroffen, die ich hatte. Vielen Dank!
Carlossierra
0

Ein Beispiel für einen Fehlerfall, den Sie gerne abfangen würden, besteht darin, dass das zu testende Objekt eine Caching-Ebene verwendet, die Daten jedoch nicht wie erforderlich beibehalten. Wenn Sie das Objekt dann abfragen, wird angezeigt: "Ja, ich habe den neuen Namen und die neue Adresse". Sie möchten jedoch, dass der Test fehlschlägt, da er nicht genau das getan hat, was er eigentlich sollte.

Alternativ (und mit Blick auf die Verletzung der Einzelverantwortung) wird angenommen, dass eine UTF-8-codierte Version der Zeichenfolge in einem byteorientierten Feld beibehalten werden muss, Shift JIS jedoch beibehalten wird. Eine andere Komponente wird die Datenbank lesen und erwartet, dass UTF-8 angezeigt wird, daher die Anforderung. Bei der Rundreise durch dieses Objekt werden dann der richtige Name und die richtige Adresse gemeldet, da sie von Shift JIS zurückkonvertiert werden. Der Fehler wird jedoch von Ihrem Test nicht erkannt. Es wird hoffentlich durch einen späteren Integrationstest erkannt, aber der springende Punkt bei Unit-Tests ist, Probleme frühzeitig zu erkennen und genau zu wissen, welche Komponente dafür verantwortlich ist.

Wenn einer von ihnen nicht tut, was er tun soll, schlägt sein eigener Testfall fehl und wir können ihn beheben und die Testbatterie erneut ausführen.

Das können Sie nicht annehmen, denn wenn Sie nicht aufpassen, schreiben Sie eine Reihe von Tests, die sich gegenseitig bedingen. Das "spart es?" test ruft die Speichermethode auf, die getestet wird, und anschließend die Lademethode, um das Speichern zu bestätigen. Das "lädt es?" test ruft die Speichermethode auf, um das Testgerät einzurichten, und dann die Lademethode, die es testet, um das Ergebnis zu überprüfen. Beide Tests stützen sich auf die Richtigkeit der Methode, die sie nicht testen, dh, keiner von beiden testet tatsächlich die Richtigkeit der Methode, die er testet.

Der Hinweis, dass es hier ein Problem gibt, ist, dass zwei Tests, die angeblich verschiedene Einheiten testen, tatsächlich dasselbe tun . Beide rufen einen Setter gefolgt von einem Getter auf und prüfen dann, ob das Ergebnis der ursprüngliche Wert ist. Sie wollten jedoch testen, ob der Setter die Daten beibehält, und nicht, ob das Setter / Getter-Paar zusammenarbeitet. Damit Sie wissen, dass etwas nicht stimmt, müssen Sie nur herausfinden, was passiert, und die Tests korrigieren.

Wenn Ihr Code für Komponententests gut konzipiert ist, können Sie auf mindestens zwei Arten testen, ob die Daten von der getesteten Methode wirklich korrekt beibehalten wurden:

  • Verspotten Sie die Datenbankschnittstelle, und lassen Sie Ihre Verspottung die Tatsache aufzeichnen, dass die richtigen Funktionen mit den erwarteten Werten aufgerufen wurden. Dies testet die Methode, was es soll, und ist der klassische Unit-Test.

  • Übergeben Sie ihm eine aktuelle Datenbank mit genau der gleichen Absicht , um aufzuzeichnen, ob die Daten korrekt gespeichert wurden oder nicht. Aber anstatt eine verspottete Funktion zu haben, die nur sagt "Ja, ich habe die richtigen Daten", liest Ihr Test direkt aus der Datenbank zurück und bestätigt, dass er korrekt ist. Dies ist möglicherweise nicht der reinste Test, da eine gesamte Datenbank-Engine eine ziemlich große Sache ist, um einen verherrlichten Schein zu schreiben, wobei ich mit größerer Wahrscheinlichkeit eine Feinheit übersehen kann, die einen Test bestanden hat, obwohl etwas nicht stimmt (also zum Beispiel ich) sollte nicht dieselbe Datenbankverbindung zum Lesen verwenden, die zum Schreiben verwendet wurde, da möglicherweise eine nicht festgeschriebene Transaktion angezeigt wird. Aber es testet das Richtige, und zumindest weißt du, dass es genau ist implementiert die gesamte Datenbankschnittstelle, ohne dass ein Mock-Code geschrieben werden muss!

Es ist also nur ein Detail der Testimplementierung, ob ich die Daten von JDBC aus der Testdatenbank lese oder ob ich die Datenbank verspotte. In beiden Fällen kann ich das Gerät besser testen, indem ich es isoliere, als wenn ich zulasse, dass es mit anderen falschen Methoden in derselben Klasse konspiriert, um auch dann richtig auszusehen, wenn etwas nicht stimmt. Aus diesem Grund möchte ich auf bequeme Weise überprüfen, ob die korrekten Daten erhalten geblieben sind, und nicht der Komponente vertrauen, deren Methode ich teste.

Wenn Ihr Code nicht für Komponententests geeignet ist, haben Sie möglicherweise keine andere Wahl, da das Objekt, dessen Methode Sie testen möchten, die Datenbank möglicherweise nicht als injizierte Abhängigkeit akzeptiert. In diesem Fall ändert sich die Diskussion über den besten Weg, das zu testende Gerät zu isolieren, in eine Diskussion darüber, wie nahe es möglich ist, das zu testende Gerät zu isolieren. Die Schlussfolgerung ist jedoch die gleiche. Wenn Sie Verschwörungen zwischen fehlerhaften Einheiten vermeiden können, können Sie, vorbehaltlich der verfügbaren Zeit und aller anderen Faktoren, die Sie für effektiver halten, Fehler im Code finden.

Steve Jessop
quelle
0

So mache ich das gerne. Dies ist nur eine persönliche Entscheidung, da sich dies nicht auf das Ergebnis des Produkts auswirkt, sondern nur auf die Art und Weise, wie es hergestellt wird.

Dies hängt von den Möglichkeiten ab, in der Lage zu sein, Scheinwerte in Ihre Datenbank aufzunehmen, ohne dass Sie die Anwendung testen.

Angenommen, Sie haben zwei Tests: A - Lesen der Datenbank B - Einfügen in die Datenbank (abhängig von A)

Wenn A besteht, sind Sie sicher, dass das Ergebnis von B von dem in B getesteten Teil und nicht von den Abhängigkeiten abhängt. Wenn A fehlschlägt, können Sie ein falsches Negativ für B haben. Der Fehler kann in B oder in A liegen. Sie können nicht sicher sein, bis A einen Erfolg zurückgibt.

Ich würde nicht versuchen, einige Abfragen in einem Unit-Test zu schreiben, da Sie die DB-Struktur hinter der Anwendung nicht kennen sollten (oder sie sich ändern könnte). Dies bedeutet, dass Ihr Testfall an Ihrem Code selbst scheitern könnte. Es sei denn, Sie schreiben einen Test für den Test, aber wer wird den Test für den Test testen ...

AxelH
quelle
-1

Am Ende kommt es darauf an, was Sie testen möchten.

Manchmal reicht es aus, die bereitgestellte Schnittstelle Ihrer Klasse zu testen (z. B. Datensatz wird erstellt und gespeichert und kann später, möglicherweise nach einem "Neustart", abgerufen werden). In diesem Fall ist es in Ordnung (und normalerweise eine gute Idee), die bereitgestellten Methoden zum Testen zu verwenden. Auf diese Weise können die Tests wiederverwendet werden, wenn Sie beispielsweise den Speichermechanismus ändern möchten (andere Datenbank, In-Memory-Datenbank zum Testen, ...).

Beachten Sie, dass es Ihnen nur wichtig ist, dass die Klasse ihren Vertrag einhält (in diesem Fall Datensätze erstellen, speichern und abrufen), und nicht, wie dies erreicht wird. Wenn Sie Ihre Komponententests intelligent erstellen (nicht direkt an einer Schnittstelle, sondern direkt an einer Klasse), können Sie verschiedene Implementierungen testen, da diese auch den Vertrag der Schnittstelle einhalten müssen.

Auf der anderen Seite müssen Sie in einigen Fällen tatsächlich überprüfen, wie die Daten erhalten bleiben (möglicherweise hängt ein anderer Prozess davon ab, ob die Daten in dieser Datenbank das richtige Format aufweisen?). Jetzt können Sie sich nicht nur darauf verlassen, dass der richtige Datensatz von der Klasse zurückgegeben wird, sondern müssen auch sicherstellen, dass er korrekt in der Datenbank gespeichert ist. Der direkteste Weg dazu ist das Abfragen der Datenbank.

Jetzt interessiert es Sie nicht nur, ob die Klasse ihren Vertrag einhält (erstellen, speichern und abrufen), sondern auch, wie dies erreicht wird (da persistierte Daten an einer anderen Schnittstelle haften müssen). Da die Tests jetzt jedoch sowohl von der Klassenschnittstelle als auch vom Speicherformat abhängig sind , ist es schwierig, sie vollständig wiederzuverwenden. (Manche nennen diese Art von Tests "Integrationstests".)

hoffmale
quelle
@Downvoters: Können Sie mir Ihre Gründe nennen, damit ich die Aspekte verbessern kann, von denen Sie glauben, dass meine Antwort fehlt?
Hoffmale