- Advertisement -spot_img
HomeSoftware TestingTesten von Microservices in agilen Projekten

Testen von Microservices in agilen Projekten

- Advertisement -spot_img

Autor: Harry Sneed

In agilen Entwicklungsprojekten stoßen zwei entgegengesetzte Faktoren, Zeit und Qualität, aufeinander und verursachen nicht selten Projektabbrüche. In einem Release-Intervall, das auf maximal drei Wochen beschränkt ist, sollten neue lauffähige Services konzipiert, implementiert und verifiziert werden. Das ist aber nur möglich, wenn der Code weder zu groß noch zu komplex ist. Der vorliegende Beitrag zur Entwicklung von Microservices schlägt vor, die Größe der Microservices nach dem Testaufwand zu richten. Dieser wird anhand eines Prototyps festgestellt.

Was sind Microservices?

Microservices sind möglichst klein gehaltene Software-Bausteine, die Anwendungsprozesse als Service ausführen können. Ziel ist es, sie in möglichst viele Anwendungen einzupassen und mit einem vertretbaren Aufwand zu testen. Microservices werden über Parameter mit den aufrufenden Hauptservices oder Applikationen verbunden. Dafür gibt es unterschiedliche Konventionen, z. B. WSDL und REST. Diese Service-Schnittstellendefinitionen lassen sich ebenso parsen und messen wie die Services selbst.

Agile Entwicklung mit Microservices

Moderne Softwareanwendungen werden zum einen von der Softwarearchitektur der Microservices geprägt und zum anderen von der agilen Softwareentwicklung. Wir wollen Softwaresysteme aus vorgefertigten Microservices zusammen- und diese Services über agile Entwicklungsprojekte bereitstellen. Die Zusammenstellung von Microservices kann aus einer Bibliothek vorgefertigter Services erfolgen. Diese Services können gekauft, aus Open-Source Quellen geliehen, aus bestehenden Systemen herausgeschnitten oder neu entwickelt werden Hauptsache ist, sie sind schon vorhanden, wenn die eigentliche Entwicklung beginnt. Entwickler:innen brauchen sie anschließend nur an ihre Anwendungssoftware anbinden und nach Bedarf aufrufen.


Microservice-Auswahl

Wichtig bei der Auswahl der Microservices ist, dass die Entwickler:innen einer Anwendung davon ausgehen können, dass die dazugehörigen Services bereits ausgetestet sind. Falls doch noch Fehler beim Anwendungstest auftreten, kann man annehmen, die Fehler liegen in der Nutzung der Services und nicht in den Services selbst. Dafür müssen die Services nach strengen Testkriterien getestet werden. Für den Code, gibt es diverse Testmessungskriterien wie:

  • Anweisungsüberdeckung = Ausführung aller Anweisungen
  • Zweigüberdeckung = Durchlauf aller Zweige,
  • Pfadüberdeckung = Durchlauf aller Pfade,
  • Parameterüberdeckung = Nutzung aller Parameter,
  • Parameterkombinationsüberdeckung = Durchspiel aller Parameterkombinationen und
  • Objektüberdeckung = Erzeugung aller möglichen Objektinstanzen.

Die Erfüllung jener Kriterien wird durch eine dynamische Analyse des jeweiligen Services kontrolliert. Ein Service darf erst als getestet gelten, wenn ein Kriterium zu 100% erfüllt ist. Beim Test von Services darf es keine Kompromisse geben. Entweder sind die Testendkriterien erfüllt oder nicht.


Microservice-Bibliothek

Die Microservice-Bibliothek muss stehen, ehe die Applikationsentwicklung beginnen kann. Das gilt auch für agile Entwicklungsprojekte. Die ersten Releases sind also die Services selbst. Entweder werden sie neu entwickelt oder sie werden aus einem bestehenden Code herausgeschnitten, d. h. recycelt. In beiden Fällen müssen sie in der Regel als erstes refaktoriert 1 werden, um die agilen Testbarkeitskriterien zu erfüllen. (siehe Abb. 1)

Die refaktorierten Services müssen dabei mehrfach angepasst werden, bis ihre Gast-Schnittstelle mit der Host-Schnittstelle übereinstimmt. Das gleiche gilt für neu entwickelte Services, die mehrfach erweitert werden müssen, bis sie alle Anforderungen erfüllen.

Bei neu entwickelten Services ist die erste Version lediglich ein Prototyp und noch kein Produkt. Ein Prototyp wird erst zum Produkt wenn er ausreichend getestet worden ist. Dabei stellt sich die Frage nach dem Testaufwand. Wie viel Zeit ist erforderlich, um einen einzelnen Service ausreichend zu testen?

Abb. 1 Service-Hierachie


Testaufwand als entscheidender Faktor

Der Testaufwand pro Microservice hängt von der Größe und Komplexität des Service ab. Wie groß und wie komplex darf ein Microservice also sein? Und wie wird die Größe gemessen?

Wenn die Codegröße oder die Codekomplexität den Grenzwert überschreitet, passt der Testaufwand für den betreffenden Service nicht mehr in den agilen Zeitrahmen hinein. Die Zeit, die benötigt wird, um ein Service zu fertigen, steht im logarithmischen Verhältnis zum Aufwand, den es braucht, z. B. Zeit = 2,5 (MM) 0,38

Die Zeit wird in Kalendertage ausgedrückt, der Aufwand wird in Personentage gezählt. Also müssen wir erst den Aufwand schätzen, um zur Zeit zu kommen. Der Aufwand für einen Servicetest hängt von der Größe und Komplexität des Services sowie von der Produktivität der Tester:innen ab.

Testaufwand = Servicegröße * Servicekomplexität / Testproduktivität

Die Servicegröße kann in Codezeilen, Codeanweisungen, Codezweigen, Codepfaden oder in Schnittstellenparameter gemessen werden. Wichtig ist, dass wir immer das gleiche Größenmaß für sämtliche Services verwenden. Die Servicekomplexität ist der Mittelwert der Ablauf-, Daten-, Datennutzungs-, Schnittstellen- und Testkomplexität.

Wenn wir die Codegröße messen wollen, müssen wir zunächst die Größe durch die Komplexität justieren und sie dann mit der justierten Größe in unsere Test-Produktivitätstabelle eingeben, um die Anzahl der Personentage zu ermitteln. Bei einem Service mit einer Größe von 400 Anweisungen und einer Komplexität von 1,2 benötigen wir einen Testaufwand von 48 Personentagen mit einer Testproduktivität von 40 getesteten Anweisungen pro Personentag.

Die 48 Personentage entsprechen circa 10 Kalendertagen. Der Unit-Test beansprucht rund die Hälfte der Releasezeit. Das bedeutet, dass wir für die Bereitstellung dieses Service insgesamt 20 Kalendertage benötigen.

Mit einem geplanten Release-Intervall von 22 Kalendertagen würden wir es gerade noch schaffen. Wäre der Release-Intervall nur 15 Kalendertage, könnten wir nicht rechtzeitig fertig werden und müssten entweder die Testkriterien schwächen oder die Sprintzeit verlängern.

Da agile Teams sehr ungern ihre ursprünglich vereinbarten Releasezyklen ändern, wird das Team höchstwahrscheinlich die Qualität des Tests opfern. In meiner Arbeit habe ich schon oft erlebt, dass agile Projekte mit hohen Verlusten abgebrochen wurden. Dieses unglimpfliche Ende wäre zu vermeiden gewesen, wenn zunächst ein Prototyp gebaut und gemessen worden wäre.

Wie groß darf ein Microservice sein?

Der Code, den wir als Microservice verwenden möchten, darf ein gewisses Grenzmaß für seine Größe – sei es in Anweisungen, Data-Points, Object-Points oder Function-Points gemessen – nicht überschreiten. Ausschlaggebend für die Bestimmung der Größe ist der Aufwand, den wir betreiben müssen, um den Service zu testen. Es ist damit zu rechnen, dass der Servicetest mehrfach wiederholt werden muss. Deshalb ist es notwendig, dass er in den Releasezyklus der agilen Entwicklung hineinpasst, d. h. 20 Tage nicht überschreitet. Daraus ergeben sich zwei Fragen: Wie messen wir die Größe und Komplexität eines Microservice? Und wie sichern wir, dass das Grenzmaß nicht überschritten wird?

Automatisierte Messwerkzeuge

Die Antwort auf die erste Frage lautet: mit automatisierten Messwerkzeugen. Damit dauert es nur eine Minute, die Größe des Service zu messen. Das Werkzeug zählt die Anweisungen, Zweige, Pfade, Parameter und Objekte im Code und leitet daraus die Anzahl der erforderlichen Testfälle ab. Die Zahlen werden den Testenden in einem Testmetrik-Bericht angezeigt. Darin ist zu erkennen, welche Grenzwerte überschritten worden sind.

Intelligente Editoren

Um sicherzustellen, dass der Grenzwert nicht überschritten wird, können intelligente Editoren verwendet werden. Ein intelligenter, regelbasierter Editor kann erkennen, wo ein Grenzwert überschritten wird und falls dies passiert – dem Verfasser des Textes eine Warnmeldung anzeigen. Der Source-Text wird nicht gespeichert, bis der Entwickler den Text korrigiert hat. So spart man sich den Aufwand für die Refaktorierung des Services.

Eigenschaften eines Microservice

Als aufrufbare Softwarekomponente mit einer begrenzten Funktionalität und einer webbasierten Schnittstelle nach außen empfängt ein Microservice ein Request und gibt daraufhin ein Response. Das Format der Schnittstelle ist normiert, d. h. das Microservice ist in jeder Umgebung, die diese Art der Kommunikation zwischen der verteilten Komponente unterstützt, erlaubt. Neben der Verarbeitung von Aufträgen hat das Service eine vorgeschriebene Behandlung von Fehlerzuständen – Exception Handling.

In einer serviceorientierten Architektur werden Services verwendet, um gemeinsame, wiederholte Funktionen zu implementieren. In einem Microservice sind die Funktionen so tief gegliedert, dass jede Funktion einer einzelnen Operation entspricht, die unabhängig aufrufbar ist. Die erste Herausforderung liegt also darin, die Anwendungsfunktionen soweit aufzubrechen, dass jede Grundfunktion in einer Operation gekapselt werden kann. Ein Microservice umfasst mehrere solcher Operationen, die alle die gleichen Daten verwenden. Die zweite Herausforderung ist, die vielen elementaren Funktionen so miteinander zu verbinden, dass sie ein Ganzes bilden. Die elementaren Funktionen eines Reisebuchungsprozesses sind beispielsweise so zusammengesetzt, dass sie eine geschlossene Handlung bzw. einen Anwendungsfall bilden. Die gleichen elementaren Funktionen können aber auch in einem anderen Prozess wie einer Buchbestellung verwendet werden.

Aufwandsbegrenzung in agilen Projekten

Der Aufwand für die Entwicklung neuer Services wird hauptsächlich vom Aufwand für den Test geprägt. Das Verhältnis ist mindestens 40:60, d. h. 40 % des Aufwandes entfallen auf den Entwurf und die Kodierung und 60 % auf den Test. Durch die Wiederverwendung alter Klassen oder Prozeduren als Services, lässt sich der Aufwand reduzieren. Da der Entwurf und ein Großteil des Coding wegfallen, reduziert sich das Verhältnis auf 20:80, d. h. 20 % für die Codeumwandlung und 80 % für den Test.

Die Entwickler:innen kommen nicht umhin, die Serviceschnittstellen mit verschiedenen Parameterkombinationen zu testen und alle relevanten Pfade zu durchlaufen. Sämtliche Anweisungen müssen mindestens einmal ausgeführt werden. Das erfordert Zeit. Je mehr Parameter, Pfade und Anweisungen es gibt, desto höher ist der Testaufwand. Damit wächst der Testaufwand proportional zur Servicegröße und -komplexität. Um den Aufwand zu begrenzen und die Projektlaufzeit zu verkürzen, muss die Größe und Komplexität der Services begrenzt bleiben. Es stellt sich also die Frage, wie viel Testaufwand sich ein agiles Projekt leisten kann, ohne den Zeitrahmen von maximal drei Wochen pro Zyklus zu sprengen. Zu bedenken ist, dass der Test nicht mehr als 50 % des Gesamtaufwandes betragen soll. Daraus folgt, dass der Test allenfalls 10 Personentage in Anspruch nehmen darf.

Abb. 2 Microservice Testrahmen

Begrenzung der Servicegröße

Die Größe eines Microservice lässt sich auf zweierlei Weise messen: Zum einen extern an der Menge der Parameter, bzw. ausgetauschter Daten und zum anderen, intern an der Länge des Codes.

Die Menge der übergebenen Daten bestimmt die Breite einer Schnittstelle. Sie ist die Anzahl einzelner Parameter im Request sowie die Anzahl einzelner Ergebnisse im Response. Je breiter die Schnittstelle ist, desto größer ist der Aufwand, sie zu generieren und zu validieren. Der Testaufwand wird durch die Menge der Ein- und Ausgaben bestimmt. Somit darf ein Service nur so viele Parameter haben, wie das Entwicklungsteam Zeit hat zu testen.

Einschränkung der Parameterzahl

Wenn es zu viele Parameter gibt, d. h. mehr als wir in der gegebenen Zeit testen können, muss die Schnittstelle überarbeitet werden. Jeder Eingangsparameter muss beim Test eines Services gezielt gesetzt werden. Falls dies nicht zu automatisieren ist, muss es manuell erfolgen. Ein Eingangsparameter in einem Request zu versorgen und ein Ausgangsparameter in dem entsprechenden Response zu prüfen, kostet bis zu einer halben Stunde Zeit. In einer Operation mit vier Parametern sind das 4 x 4 = 16 Parameter-Kombinationen. Wenn ein Service fünf solcher Operationen hat, sind das 5 x 16 = 80 Testfälle aus der Sicht der Daten.

Einschränkung der Codegröße

Die Größe des Codes wird zum Zweck des Testens anhand der Anzahl Pfade und den dazugehörigen Anweisungen bestimmt. In einem Java-System für die Abrechnung von Arztkosten gab es z. B. 2.745.224 Codezeilen, 1.094.684 Anweisungen, 262.389 Zweige und 124.364 Pfade. Mit zwei Testfällen pro Pfad brauchten wir 248.728 Testfälle, um alle Services auszutesten. Bei einer solch hohen Pfadanzahl muss eine systematische Refaktorierung zur Reduzierung erfolgen. Die Refaktorierung ist nötig, um eine Überarbeitung in einem agilen Projekt zu erlauben.

Fazit

Die Servicegröße muss sich nach der verfügbaren Zeit richten, nicht umgekehrt. Wenn 20 Tage pro Service vorgesehen sind und davon 10 Tage für den Test aufgewendet werden, können in einem Release, bzw. in einem Sprint, nicht mehr als 100 Testfälle pro Service getestet werden. Die zur Verfügung stehende Testzeit bestimmt, wie groß, bzw. wie komplex ein Service sein darf.

Artikel teilen
Redaktion
Redaktion
Die SQ-Magazin Redaktion ist Ihr Ansprechpartner für alle Fragen, Anregungen und Ideen rund um das SQ-Magazin. Kontaktieren Sie uns gern unter redaktion@sq-magazin.de Wir freuen uns auf Ihre Nachricht!
- Advertisement -Certified DevOps Portfolio
Neueste Artikel
Weitere interessante Artikel
- Advertisement -spot_img
[js-disqus]