Java >> Java Tutorial >  >> Java

Schreiben von Tests für Datenzugriffscode - Testen Sie nicht das Framework

Wenn wir Tests für unseren Datenzugriffscode schreiben, sollten wir dann jede Methode seiner öffentlichen API testen?

Das klingt erstmal natürlich. Wenn wir nicht alles testen, wie können wir schließlich wissen, dass unser Code wie erwartet funktioniert?

Diese Frage gibt uns einen wichtigen Hinweis:

Unser Kodex .

Wir sollten Tests nur für unseren eigenen Code schreiben.

Was ist unser eigener Kodex?

Es ist manchmal schwierig, den Code zu identifizieren, den wir testen sollten. Der Grund dafür ist, dass unser Datenzugriffscode eng mit der Bibliothek oder dem Framework integriert ist, das wir verwenden, wenn wir Informationen auf dem verwendeten Datenspeicher speichern oder Informationen daraus lesen.

Wenn wir beispielsweise ein Spring Data JPA-Repository erstellen möchten, das CRUD-Vorgänge für Todo bereitstellt Objekten, sollten wir eine Schnittstelle erstellen, die das CrudRepository erweitert Schnittstelle. Der Quellcode des TodoRepository Die Benutzeroberfläche sieht wie folgt aus:

import org.springframework.data.repository.CrudRepository;

public TodoRepository extends CrudRepository<Todo, Long> {

}

Auch wenn wir unserer Repository-Schnittstelle, dem CrudRepository, keine Methoden hinzugefügt haben interface deklariert viele Methoden, die den Klassen zur Verfügung stehen, die unsere Repository-Schnittstelle verwenden.

Diese Methoden sind nicht unser Code, da sie vom Spring Data-Team implementiert und gepflegt werden. Wir verwenden sie nur.

Wenn wir unserem Repository andererseits eine benutzerdefinierte Abfragemethode hinzufügen, ändert sich die Situation. Nehmen wir an, wir müssen alle Aufgabeneinträge finden, deren Titel mit dem angegebenen Suchbegriff übereinstimmt. Nachdem wir diese Abfragemethode zu unserer Repository-Schnittstelle hinzugefügt haben, sieht ihr Quellcode wie folgt aus:

import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;

public TodoRepository extends CrudRepository<Todo, Long> {

	@Query("SELECT t FROM Todo t where t.title=:searchTerm")
	public List<Todo> search(@Param("searchTerm") String searchTerm)
}

Es wäre leicht zu behaupten, dass diese Methode unser eigener Code ist und wir sie deshalb testen sollten. Die Wahrheit ist jedoch etwas komplexer. Obwohl die JPQL-Abfrage von uns geschrieben wurde, stellt Spring Data JPA den Code bereit, der diese Abfrage an den verwendeten JPA-Anbieter weiterleitet.

Und dennoch denke ich, dass diese Abfragemethode unser eigener Code ist, weil der wichtigste Teil davon von uns geschrieben wurde.

Wenn wir unseren eigenen Datenzugriffscode identifizieren wollen, müssen wir den wesentlichen Teil jeder Methode lokalisieren. Wenn dieser Teil von uns geschrieben wurde, sollten wir diese Methode als unseren eigenen Code behandeln.

Das ist alles ziemlich offensichtlich, und die interessantere Frage ist:

Sollen wir es testen?

Unsere Repository-Schnittstelle stellt den Klassen, die sie verwenden, zwei Arten von Methoden zur Verfügung:

  1. Es stellt Methoden bereit, die vom CrudRepository deklariert werden Schnittstelle.
  2. Es bietet eine von uns geschriebene Abfragemethode.

Sollten wir Integrationstests für das TodoRepository schreiben Schnittstelle und alle diese Methoden testen?

Nein. Wir sollten dies nicht tun, weil

  1. Die vom CrudRepository deklarierten Methoden interface sind nicht unser eigener Code. Dieser Code wird vom Spring Data-Team geschrieben und gepflegt, und sie haben dafür gesorgt, dass er funktioniert. Wenn wir nicht darauf vertrauen, dass ihr Code funktioniert, sollten wir ihn nicht verwenden.
  2. Unsere Anwendung hat wahrscheinlich viele Repository-Schnittstellen, die das CrudRepository erweitern Schnittstelle. Wenn wir uns entscheiden, Tests für die vom CrudRepository deklarierten Methoden zu schreiben Schnittstelle müssen wir diese Tests für alle Repositories schreiben. Wenn wir uns für diesen Weg entscheiden, werden wir viel Zeit damit verbringen, Tests für den Code eines anderen zu schreiben, und ehrlich gesagt, es lohnt sich nicht.
  3. Unser eigener Code könnte so einfach sein, dass das Schreiben von Tests für unser Repository keinen Sinn macht.

Mit anderen Worten, wir sollten uns darauf konzentrieren, eine Antwort auf diese Frage zu finden:

Sollten wir Integrationstests für unsere Repository-Methoden schreiben (Methoden, die von uns geschrieben wurden), oder sollten wir nur End-to-End-Tests schreiben?

Die Antwort auf diese Frage hängt von der Komplexität unserer Repository-Methode ab. Mir ist bewusst, dass Komplexität ein ziemlich schwammiges Wort ist, und deshalb brauchen wir eine Art Richtlinie, die uns hilft, den besten Weg zum Testen unserer Repository-Methoden zu finden.

Eine Möglichkeit, diese Entscheidung zu treffen, besteht darin, über den Arbeitsaufwand nachzudenken, der erforderlich ist, um jedes mögliche Szenario zu testen. Dies ist sinnvoll, weil:

  1. Es erfordert weniger Arbeit, Integrationstests für eine einzelne Repository-Methode zu schreiben, als dieselben Tests für die Funktion zu schreiben, die die Repository-Methode verwendet.
  2. Wir müssen sowieso End-to-End-Tests schreiben.

Daher ist es sinnvoll, unsere Investitionen (Zeit) zu minimieren und unsere Gewinne (Testabdeckung) zu maximieren .

Dies ist natürlich leichter gesagt als getan, da jede Situation einzigartig ist und es unmöglich ist, Regeln zu finden, die in jeder Situation gültig wären.

Wir können beginnen, indem wir die Antworten auf die folgenden Fragen finden:

  • Ist die Funktion, die unsere Repository-Methode verwendet, einfach oder komplex? Wir können uns ein Bild davon machen, indem wir weitere Fragen stellen:
    • Gibt die Funktion nur Informationen zurück, die von einem Datenspeicher abgerufen werden, oder ändert sie diese?
    • Wie viele Abhängigkeiten hat die Funktion?
  • Wie viele Tests müssen wir für unser Feature schreiben, wenn wir alle möglichen Szenarien testen wollen?
  • Wie viele Tests müssen wir für unsere Repository-Methode schreiben, wenn wir alle möglichen Szenarien testen wollen?

Nachdem wir die Antworten auf diese Fragen gefunden haben, können wir unsere Kapitalrendite maximieren, indem wir diese Regeln befolgen:

  • Wenn wir alle möglichen Szenarien testen können, indem wir nur ein paar End-to-End-Tests schreiben, sollten wir unsere Zeit nicht damit verschwenden, Integrationstests für unsere Repository-Methode zu schreiben. Wir sollten End-to-End-Tests schreiben, die sicherstellen, dass die Funktion wie erwartet funktioniert.
  • Wenn wir mehr als ein paar Tests für unsere Repository-Methode schreiben müssen, sollten wir Integrationstests für unsere Repository-Methode schreiben und nur ein paar End-to-End-Tests (Smoke-Tests) schreiben.

Zusammenfassung

Dieser Blogpost hat uns zwei Dinge gelehrt:

  • Wir sollten unsere Zeit nicht damit verschwenden, Tests für ein Datenzugriffs-Framework (oder eine Bibliothek) zu schreiben, die von jemand anderem geschrieben wurden. Wenn wir diesem Framework (oder dieser Bibliothek) nicht vertrauen, sollten wir es nicht verwenden.
  • Manchmal sollten wir auch keine Integrationstests für unseren Datenzugriffscode schreiben. Wenn der getestete Code einfach genug ist (wir können alle Situationen abdecken, indem wir ein paar End-to-End-Tests schreiben), sollten wir ihn testen, indem wir End-to-End-Tests schreiben.

Java-Tag