Ruby-Programmierung: Testing

Zurück zum Inhaltsverzeichnis.

Für das automatisierte Testen des Programms existiert eine große Anzahl an Bibliotheken für Ruby. Neben der in der Standardbibliothek enthaltenen Bibliothek Test::Unit ist vor allem RSpec beliebt. Diese Seite soll sich insbesondere mit dem allgemeinen Ablauf von Test Driven Development (TDD) beschäftigen anhand eines Beispiels, das Test::Unit verwendet. Mit der Version Ruby 2.0 wurde Test::Unit durch Minitest ersetzt, die Nutzung ist jedoch noch möglich.

Test Driven Development Bearbeiten

Es handelt sich dabei um eine Technik, die automatisierte Tests in den Entwicklungsprozess einbindet und ist in der Rubyentwicklergemeinschaft weit verbreitet. Dabei kommt es weniger darauf an, welche Bibliothek verwendet wird, sondern vielmehr, dass getestet wird. Dabei gibt es verschiedene Möglichkeiten die Güte einer Testsuite (Sammlung von Tests) zu messen: zum Beispiel Codecoverage. An dieser Stelle sei jedoch nur kurz darauf verwiesen, dass Übung den Umgang mit Tests sehr erleichtert.

Falls Sie bisher Aufgaben dieses Buches oder andere Probleme gelöst haben, sah Ihr Ablauf wahrscheinlich in etwa so aus. Nachdem sie eine entsprechende Funktionalität dem Programm hinzugefügt haben, benutzten Sie Ihr Programm, um zu überprüfen, ob es funktioniert. Die Problematik dieser Reihenfolge ist recht offensichtlich, da es nicht möglich ist alle Teile des Programms manuell zu testen und außerdem dauern manuelle Tests sehr lange und können nicht häufig durchgeführt werden.

TDD kehrt diese Reihenfolge um. Bevor man Funktionalität dem Programm hinzufügt wird ein automatisierter Test geschrieben, der das Programm auf diese Funktionalität hin überprüft, danach wird das Programm erweitert, bis die Tests nicht mehr fehlschlagen. In dem kommenden Beispiel soll unter Verwendung von automatisierten Tests eine einfache Applikation entwickelt werden.

Notizbuch Bearbeiten

Es soll eine einfache Applikation entwickelt werden, die es erlaubt, Notizen zu erzeugen, anzuzeigen und zu löschen. Dafür wird eine Ordnerstruktur verwendet, wie sie bei Rubygems üblich ist und in dem entsprechenden Kapitel ausführlicher behandelt wird. Der Projektordner besteht dabei im wesentlichen aus zwei Unterordnern: lib/ und test/. In lib/ wird die Funktionalität des Programms beschrieben, Objekte definiert usw., während in test/ die Tests der korrespondierenden Programmteile liegen.

Zuerst befindet sich im Testordner eine Datei, welche die Bibliothek und eventuell andere Abhängigkeiten lädt, diese können dann alle Testdateien laden.

require "test/unit"
require_relative "../lib/notebook"

Dann wird der erste Test implementiert. Da sich das Notizbuch nur um Notizen dreht, wird dieses Objekt das Einzige sein, für den Tests existieren. Test::Unit funktioniert über das Anlegen einer Testklasse, deren Instanzmethoden als Tests ausgeführt werden. Die spezielle Methode setup wird am Anfang der Tests aufgerufen und dient dazu, die Tests vorzubereiten zum Beispiel durch das anlegen von Objekten.

require_relative "../test_helper"

class NoteTest < Test::Unit::TestCase
  CAPTION, CONTENT = "TDD", "Test Driven Development ist eine tolle Sache"
  
  def setup
    @note = Notepad::Note.new(CAPTION, CONTENT)
  end
  
  def test_caption
    assert @note.caption == CAPTION
  end
end

Das Ausführen dieses Tests führt wie erwartet zu einem Fehler, dass Notepad::New nicht existiert. Daher müssen wir an dieser Stelle die Klasse definieren.

module Notebook
  class Note
    def initialize(caption, content)
      @caption = caption
      @content = content
    end
  end
end

Das erneute Ausführen des Tests führt zu einem neuen Fehler, dass es keine Instanzmethode caption gibt. Diese kann leicht über attr_reader angelegt werden.

module Notebook
  class Note
    attr_reader :caption
  end
end

Danach läuft der Test erfolgreich durch und es danach leicht Möglich neue Tests für den Content einer Note, sowie für das ganze Notebook zu erstellen.

Der Vorteil von TDD zeigt sich besonders deutlich, wenn Sie bisher bereits größere Aufgaben bewältigt haben. Sie kamen dann eventuell an eine Stelle, dass sie ihren Code anders organisieren und umstrukturieren müssen, dann ist es von Vorteil, dass automatisierte Tests die Funktionalität des Programms vor und nachdem Refactoring garantieren.