Eine Legacy-Rails-App ohne Tests zu ändern, gleicht einer Operation ohne Röntgenbild. Sie wissen nicht, was Sie kaputt machen, bis es zu spät ist. Veraltete Ruby-Versionen, fehlende Dokumentation und eng gekoppelter Code machen Updates riskant. Regressionstests sind das Sicherheitsnetz, das dieses Risiko kontrollierbar macht.

Wie bereiten Sie eine Legacy-App auf Tests vor?

Codebestand analysieren

Starten Sie mit rake stats. Der Befehl zeigt die Grösse der App: Codezeilen, Controller, Modelle, vorhandene Tests. Viele Legacy-Apps haben Boilerplate-Tests mit minimaler Abdeckung.

Konzentrieren Sie die Analyse auf:

  • Hochkomplexe Bereiche: Lange Methoden, verschachtelte Bedingungen
  • Eng gekoppelter Code: Modelle, die direkt auf andere Modelle zugreifen
  • Nicht dokumentierte Abschnitte: Code ohne Kommentare oder Tests

Prüfen Sie Gemfile und Gemfile.lock auf veraltete Bibliotheken. Achten Sie auf Warnungen bei bundle install oder beim Rails-Start. Diese deuten auf Kompatibilitätsprobleme mit modernen Testwerkzeugen hin.

Dokumentieren Sie alles: kritische Funktionen, bekannte Probleme, Abschnitte, die Refactoring brauchen. Das ist Ihr Fahrplan.

Testumgebung einrichten

  • Separate Konfiguration in config/environments/test.rb
  • Dedizierte Testdatenbank, die die Produktions-Engine spiegelt
  • Umgebungsvariablen und Stubs für externe Dienste
  • Transaktionsbasierte Fixtures und Datenbank-Bereinigung
  • Logging speziell für Tests konfigurieren

RSpec oder Minitest?

FrameworkVorteileNachteile
RSpecFlexible Syntax, starke Matcher, grosse CommunityMehr Setup-Aufwand
MinitestLeichtgewichtig, schnell, in Rails integriertWeniger ausdrucksstark

Für Legacy-Projekte empfehlen wir RSpec: Die ausdrucksstarke Syntax macht Tests für schlecht dokumentierten Code lesbarer. Installation:

# Gemfile
gem 'rspec-rails', group: [:development, :test]

# Terminal
bundle install
rails generate rspec:install

Wie fügen Sie Tests schrittweise hinzu?

Schritt 1: Kritische Funktionen identifizieren

Priorisieren Sie nach Geschäftsrisiko:

  1. Zahlungsabwicklung: Alles, was Geld bewegt
  2. Benutzerauthentifizierung: Login, Session-Management, Passwort-Reset
  3. Kern-Geschäftslogik: Die Prozesse, die Umsatz generieren
  4. Datenimporte/-exporte: Schnittstellen zu externen Systemen

Für jede Funktion dokumentieren Sie: Geschäftliche Bedeutung, erwartetes Verhalten, bekannte Randfälle.

Schritt 2: Unit-Tests für Kernlogik

Beginnen Sie mit den wichtigsten Methoden:

# spec/services/payment_service_spec.rb
RSpec.describe PaymentService do
  describe '#process' do
    it 'charges the correct amount' do
      result = described_class.new(amount: 1500.75, currency: 'CHF').process
      expect(result).to be_success
      expect(result.charged_amount).to eq(1500.75)
    end

    it 'rejects negative amounts' do
      result = described_class.new(amount: -100, currency: 'CHF').process
      expect(result).to be_failure
    end

    it 'handles zero amount' do
      result = described_class.new(amount: 0, currency: 'CHF').process
      expect(result).to be_failure
    end
  end
end

Schritt 3: Integrationstests für Workflows

Testen Sie End-to-End-Prozesse: Registrierung, Login, Bestellung abschliessen. Decken Sie auch Fehlerfälle ab (Zahlungsfehler, ungültige Eingaben).

Schritt 4: Tests ausführen und Fehler analysieren

Fehler sind zu erwarten. Kategorisieren Sie:

  • Echte Bugs: Code verhält sich falsch
  • Test-Probleme: Test ist fehlerhaft geschrieben
  • Kompatibilitätsprobleme: Veraltete Gems kollidieren mit Testwerkzeugen

Beheben Sie kritische Bugs zuerst. Widerstehen Sie der Versuchung, Tests anzupassen, um fehlerhaftes Verhalten durchzulassen.

Wie bleiben Regressionstests langfristig nützlich?

CI/CD-Integration

Automatisieren Sie die Tests:

  • Unit-Tests bei jedem Commit (schnelles Feedback)
  • Integrationstests bei Pull Requests (gründliche Prüfung)
  • GitHub Actions oder GitLab CI konfigurieren
# .github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: ruby/setup-ruby@v1
        with:
          bundler-cache: true
      - run: bundle exec rspec

Testabdeckung überwachen

SimpleCov zeigt Lücken auf:

require 'simplecov'
SimpleCov.start 'rails' do
  minimum_coverage 70
  refuse_coverage_drop
end

Steigern Sie die Mindestabdeckung schrittweise: 50 % > 60 % > 70 % > 80 %.

Tests aktuell halten

  • Tests bei jeder Feature-Änderung aktualisieren
  • Obsolete Tests für entfernte Features löschen
  • Dokumentation der Testfälle pflegen

Praktische Umsetzung: Der USEO-Ansatz

Bei Legacy-Projekten, die wir übernehmen, gehen wir Regressionstests methodisch an:

  1. Characterization Tests zuerst: Bevor wir irgendetwas ändern, schreiben wir Tests, die das aktuelle Verhalten dokumentieren, auch wenn es fehlerhaft ist. Das gibt uns ein Sicherheitsnetz für alle folgenden Änderungen.
  2. Risiko-Matrix erstellen: Wir bewerten jede Funktion nach zwei Achsen: Geschäftskritikalität und Änderungshäufigkeit. Funktionen mit hohem Wert auf beiden Achsen bekommen zuerst Tests.
  3. Test-Doubles für externe Systeme: Legacy-Apps haben oft hartcodierte Abhängigkeiten zu Drittsystemen (Payment-Gateways, E-Mail-Provider). Wir führen Adapter-Patterns ein und mocken diese in Tests, bevor wir die eigentliche Logik testen.
  4. Parallele Test-Suites: Wir trennen schnelle Unit-Tests (< 30s) von langsamen Integrationstests. Entwickler führen die schnelle Suite lokal aus, die vollständige Suite läuft in CI.

In einem kürzlichen Projekt haben wir so innerhalb von 6 Wochen von 0 % auf 72 % Testabdeckung für die geschäftskritischen Pfade aufgebaut, ohne den laufenden Betrieb zu beeinträchtigen.

FAQs

Welche Funktionen sollten zuerst getestet werden?

Funktionen mit dem höchsten Geschäftsrisiko: Zahlungsabwicklung, Authentifizierung, Kerngeschäftslogik. Analysieren Sie vergangene Bug-Reports, um problematische Bereiche zu identifizieren.

Welche Herausforderungen gibt es bei RSpec in Legacy-Apps?

Eng gekoppelter Code erschwert das Isolieren von Komponenten. Beginnen Sie mit Integrationstests auf hoher Ebene. Nutzen Sie Test-Doubles (Mocks, Stubs) für schwer testbare Abhängigkeiten. Refactoring in kleinen Schritten verbessert die Testbarkeit über Zeit.

Verwandte Artikel