comm-press: Your Drupal agency in Hamburg, Germany: Consulting, training, programming, service, webdesign and hosting

Knowledge . "git branch love-testing" - Entwicklung und systematisches Testen auf Basis von Branches

von Ralf Hendel
am

Neulich hatte ich einen Erfahrungsbericht über unser Deployment und unsere Quality Assurance auf Basis von git geschrieben.

Der Einsatz von git bietet eine Kontrolle über sämtliche Änderungen auf Dateiebene, die sich per "git push" dann gesammelt gegen ein Produktivsystem ausrollen lassen. Zur Zeit des Blogposts hatten wir bei unseren Projekten immer im sogenannten "Master"-Branch, dem Default- oder Haupt-Zweig gearbeitet. Das Arbeiten im Master-Branch funktioniert gut, so lange sprintbasiert Software-Inkremente entwickelt werden, die am Ende als Stable Release ausgerollt werden können.

Da bei einem Push immer sämtliche Änderungern eines Branches übertragen werden, stellen kurzfristige Kundenanforderungen oder Bug-Fixes, die neben dem "normalen" Sprint zeitnah auf das Livesystem übertragen werden müssen, immer eine Herausforderung dar. Außerdem haben alle Entwickler immer sämtliche Änderungen aller anderen Mitarbeiter im Repository, was unübersichtlich ist und zu Konflikten führen kann.

Arbeiten in Branches

Inzwischen arbeiten wir nur noch in Branches. Ein Branch (engl. = "Zweig" oder "Abzweigung") stellt einen Software-Ast dar. Mit dem Anlegen eines neuen Branches entsteht eine neue Verzweigung mit unterschiedlichen Datei-Ständen.

Mit "git branch [Branchname]" kann ich einen neuen Branch erzeugen und mich mit "git checkout [Branchname]" anschließend auf diesen neuen Branch "auschecken". Nachdem ich in dem neuen Branch gearbeitet habe, bestehen im Dateiensystem abhängig vom gerade gewälhlten Branch unterschiedliche Dateien.

Probiert es aus: beim ersten Mal ist es absolut beeindruckend: speichert eine Datei in einem Branch, checkt Euch auf einen anderen Branch aus und staunt, dass die Datei weg ist. Checkt Euch anschließend auf den ursprünglichen Branch zurück und die Datei ist wieder da.
Unfassbar ;-)

Test des Deployment-Prozesses durch lokales paralleles Testen.

Der Master-Branch repräsentiert den aktuell auf dem Produktiv-System laufenden Software-Stand. Sehr kleine Arbeiten können im Dev-Branch erledigt werden. Ansonsten wird jedes Leistungsmerkmal in einem eigenen Branch umgesetzt. Auf den Tickets wird der Name des Branchs mit notiert.

Für das Testing bedeutet das einen zusätzlichen Schritt mehr: Die Arbeiten gelten nicht mehr dann als abgeschlossen, wenn sie lokal getestet und gegen die zentrale Abnahme-Plattform ausgerollt sind. Vor dem Rollout muss sich ein anderes Team-Mitglied auf den Branch lokal auschecken und die Änderungen dort kontrollieren. Das wird häufig als anstrengend empfunden, weil es oft nicht funktioniert. In Wahrheit decken sich dabei aber die Schwächen im Deployment auf. Insofern stellt dieser Schritt aus meiner Erfahrung den wichtigsten Zwischenschritt beim Testen dar. Wenn der Mitarbeiter seine Arbeiten selbst in die Abnahmeumgebung ausrollt, ist die Versuchung recht groß, eine übersehene Einstellungen "mal eben schnell" über die GUI dort vorzunehmen. Beim Testen durch einen anderen Mitarbeiter ist dies nicht so wahrscheinlich, da dieser in der Regel nicht auf Anhieb weiß, um welche Einstellung es sich handelt. Er ist darauf angewiesen, dass der Deployment-Prozess funktioniert.

Erst nach erfolgreicher lokaler Abnahme werden die Änderungen in den Dev- oder den Master-Branch übernommen ("merge"). Bei git kann ich mit dem Befehl "git merge [Branchname]" den Branchname-Branch in meinen aktuellen Branch hinein "mergen". Dabei achtet git auf die Konsistenz der Dateien und schlägt unüberhörbar Alarm, wenn Merge-Konflikte auftauchen.

Keine Angst vor dem Merge Alarm

Git lässt keinen inkonsistenten Dateien-Zustand zu und verhindert ein weiteres Arbeiten, sobald Merge-Konflikte bestehen. Jeder, der mit git anfängt zu arbeiten, kennt die gefürchteten Merge-Meldungen. Am Anfang ist das unglaublich nervig. Man arbeitet ja schließlich gerade konzentriert an etwas und hat keinen Kanal offen, um sich mit lästigen Formalien zu beschäftigen.

Der erste hilflose Impuls besteht häufig darin, die eigenen Änderungen an einen externen Platz kopieren zu wollen an dem sie sicher sind, sämtliche Projektdateien zu löschen und mit einem "git clone" wieder Tabula Rasa her zu stellen.

Git ist Dein Freund. Git merged bei jedem Push oder Pull schon im Verborgenen die unterschiedlichen Dateistände ineinander. Nur wenn Konflikte bestehen, die Git nicht selbst lösen kann, ist eine Entscheidung des Anwenders erforlich. Dies tritt zum Beispiel auf, wenn ich lokal Änderungen an einer Datei habe, die auf dem Zielsystem bereits gelöscht wurde.

Es gibt unterschiedliche Merge-Tools wie z.B. meld, die die beiden Versionen gegenüberstellen und anbieten, die Änderungen in einer dritten Version zusammen zu führen. Nach dem Speichern und einem anschließendem "git commit" lassen sich die Konflikte in der Regel schnell beheben.

... Weiter im Testing

Beim Auschecken der Branches ist drush praktisch unverzichtbar. Bei kompexeren Änderungen, die mehrere Features betreffen, müssen die Features auf den Branch-Stand angepasst werden. Durch den Branch ändert sich die Datenbank nicht. Wenn ich den Branch wechsle, entstehen häufig Differenzen zwischen dem Datenbankstand innerhalb von Drupal und dem Features-Stand auf Code-Ebene. Für ein ordnungsgemäßes Funktionieren müssen also alle Features zurück gesetzt werden. Über die Drupal GUI ist das eine sehr zeitraubende Sache.

Hier ist Drush wieder mein bester Freund. Mit dem Befehl "drush fra -y && drush cc all" kann ich mit einem Befehl sämtliche Features zurück setzen ("revert") und anschließend gleich den Drupal Cache mit leeren.

Etwas umständlicher wird es, wenn Module Änderungen in der Datenbankstruktur nach sich ziehen. Hier hilft nur das Einrichten einer zusätzlichen lokalen Umgebung oder aber das Sichern und Zurückspielen der betreffenden Datenbank-Stände. Wenn ich beispielsweise ein Upgrade auf Views3 testen möchte, muss ich ein Datenbankupdate vornehmen, welches Tabellen ändert. Beim anschließenden Zurückgehen auf den ursprünglichen Stand bin ich darauf angewiesen, einen vorherigen Datenbankstand zurück spielen zu können.

Auch hier hilft Drush enorm Zeit zu sparen: mit Befehlen wie "drush bam-backup" bw. "drush bam-restore" kann ich bequem mit meinen Dumps jonglieren. Eine Übersicht der Drush Befehle findet sich hier.

Datensicherung: nicht nur etwas für Weicheier...

Sehr unangenehm ist es, wenn bestimmte Dinge lokal wie auf der Abnahmeumgebung fehlerfrei funktionieren, aber erst im Betrieb auf der Livesite ihren Dienst verweigern. Diese Tücken bestehen grundsätzlich immer, da zwei System niemals absolut identisch sind. In einem aktuellen Fall war auf einem Produktionsserver die Funktion "allow_fopen_url" gesperrt. Die Abfrage einer externen URL hat überall problemlos funktioniert - nur nicht im Livesystem...

Hier helfen die üblichen Netze: Vor jedem Rollout ein Sicherheitsupdate der Datenbank anlegen. Die Sicherung auf Dateiebene übernimmt git per sé schon. Damit lässt sich jederzeit wieder die vorherige Version herstellen und die Fehler lassen sich anschließend in Ruhe untersuchen und beheben.