Niemand wird in Abrede stellen, dass bei der Entwicklung von Business-Anwendungen im Enterprise-Bereich hohe softwaretechnische Standards einzuhalten sind. Oftmals werden diese Standards in projektübergreifend geltenden Reglements festgelegt, in denen Abweichungen nur in begründeten Ausnahmefällen zugelassen sind. Das ist auch notwendig, weil langlaufende Projekte ohne solche Regeln nicht zu pflegen wären.
In der Praxis können aber auch problematische Wechselwirkungen zwischen gängigen „Best Practices“ entstehen. So auch zwischen zwei im Allgemeinen als wünschenswert geltenden Vorgehensweisen: Der Verwendung von Stored Procedures anstelle von direkten Zugriffen auf die Datenbank und dem Anstreben einer hohen automatisierten Testabdeckung.
Mit erheblichem Aufwand verbunden
Durch die ausschließliche Verwendung von Stored Procedures wird eine weitere Ebene („Layer“) zwischen dem Data Access Layer der Anwendung und der Datenbank geschaffen. Das bringt einige Vorteile und Möglichkeiten mit sich, aber auch einen nicht unerheblichen Nachteil: Mit regulären Unit Tests, d. h. ohne Zuhilfenahme einer Testdatenbank, lässt sich nicht sicherstellen, dass eine Stored Procedure wirklich das tut, was sie tun soll.
Nun könnte man zwar dagegen halten, dass dies beim direkten Zugriff auf die Datenbank auch nicht anders wäre. Tatsächlich kann man bei modernen OR-Mappern wie dem Entity Framework aber davon ausgehen, dass die jeweiligen Komponenten bereits vom Hersteller so ausgiebig getestet wurden, dass so gut wie immer gültige SQL-Statements erzeugt werden. Bei selbst geschriebenen Stored Procedures sind Integrationstests im Sinne einer annähernd umfassenden Testabdeckung dagegen unumgänglich.
Integrationstests gehören zu den zeitaufwendigsten Arten von Tests. Dazu gehört in diesem Fall nicht nur das Generieren geeigneter Testdaten, sondern eine Doppelimplementierung der typischen CRUD-Funktionen (Create, Read, Update, Delete), üblicherweise mittels konventionellen SQL-Statements innerhalb des Testprojekts. Nur so kann jede auf einer Stored Procedure basierende Methode isoliert von den anderen Methoden getestet werden. Des Weiteren muss darauf geachtet werden, dass etwaige parallel laufende Tests nicht die gleichen Daten verändern. Auch mit der Bereinigung der Datenbank muss man sich befassen.
Die Praxis: Copy-and-Paste
Doch das ist noch längst nicht alles: Selbst kleinere Business-Anwendungen greifen auf eine hohe zweistellige bis dreistellige Anzahl von Datenbanktabellen zu. Wenn man den beschriebenen Weg einschlagen will oder muss, bedeutet das also, dass man bereits für die CRUD-Operationen hunderte von Stored Procedures und noch mehr Integrationstests schreiben wird.
Was aber natürlich niemand tut – schließlich gibt es zwischen vielen Datenentitäten so große Ähnlichkeiten, dass man das Rad nicht ständig neu erfinden muss. In den Projektalltag übersetzt bedeutet das fast immer Copy-and-Paste in großem Stil. Das muss nicht schlecht sein, schließlich wird dadurch sichergestellt, dass die Tests autark voneinander lauffähig und veränderbar sind.
Die Copy-and-Paste-Methode bringt aber auch eine Reihe von Nachteilen mit sich:
1. Es handelt sich immer noch um manuell erstellte Tests, deren Qualität maßgeblich vom Detailwissen eines bestimmten Entwicklers zu einem bestimmten Zeitpunkt abhängig ist.
2. Sofern die Tests nicht von Anfang an mit Bedacht organisiert werden, ist später oft kaum mehr zu erkennen, welche Tests für eine bestimmte Entität essentiell wichtig sind.
3. Wenn ein Test sich zu einem späteren Zeitpunkt als fehlerhaft oder unvollständig erweist, müssen auch etliche Kopien korrigiert werden. Es besteht die Gefahr, dass einige davon inzwischen so verändert wurden, dass sie übersehen werden.
4. Zwischen Entitäten gibt es oft Abhängigkeiten. Wenn eine der beteiligten Entitäten verändert wird, kann das auf zahlreiche Tests Auswirkungen haben, ohne dass diese Tests verlässlich „rot“ werden. Auf diese Weise kann eine trügerische Gewissheit entstehen.
Copy-and-Paste gilt nicht ohne Grund als ein Anti-Pattern – und als solches erweist es sich auch hier, sobald größere Veränderungen an wichtigen Datenbankentitäten vorgenommen werden. Eben dies dürfte in vielen langlaufenden Projekten nicht die Ausnahme, sondern die Regel sein. In den weiteren Teilen dieser Artikelserie soll ein Weg aufgezeigt werden, mit dem sich die durchgängige Verwendung von Stored Procedures und das Anstreben einer weitgehenden automatisierten Testabdeckung besser miteinander in Einklang bringen lassen.