Open Source im professionellen Einsatz

Newsletter abonnieren
Seite durchsuchen

HEFTARCHIV | NEWS | E-BIBLIOTHEK | VIDEO | BLOGS | WHITEPAPER | EVENTS | ACADEMY | ABO | SHOP

user friendly

  Home  »  Heft & Abo  »  Heftarchiv  »  2005  »  04  »  Alles unter Kontrolle  

RSS-Feed der aktuellen News von Linux-Magazin Online Folgen Sie Linux-Magazin Online auf Twitter
Diesen Artikel druckenDiesen Artikel weiterempfehlen Diesen Artikel kommentieren Newsletter abonnieren
Share/Bookmark

Tests ablaufen lassen

Die so genannten Testrunner führen die Tests aus und bereiten zudem die Ergebnisse übersichtlich auf. Standardmäßig verwendet »Test::Unit« den Testrunner der Kommandozeile (siehe Abbildung 2).

Für eine grafische Ausgabe ist einfach der Testrunner für das gewünschte Userinterface zu wählen, zum Beispiel der TK-Testrunner für eine Darstellung mit TK beziehungs-weise der GTK-2-Testrunner für die Anzeige mit dem GTK-2-Toolkit (siehe Abbildung 3). Einen solchen grafischen Testrunner kann natürlich nur verwenden, wer die Ruby-Module für das entsprechende Toolkit installiert hat.

Der folgende Code bindet erst den Testrunner ein und führt danach dessen »run«-Methode aus:

require 'test/unit/ui/gtk2/testrunner'
Test::Unit::UI::GTK2::TestRunner.run(Test)

Der übergebene Parameter (»Test«) darf ein einzelner Test oder eine ganze Testsuite sein. Es ist wichtig, den Testrunner nur auf der obersten Ebene anzugeben, da die einzelnen Tests sonst mehrfach durchlaufen.

Testsuites fassen mehrere Unit-Tests sowie bereits bestehende Testsuites zu einem Paket zusammen. Damit bleibt auch in einem größeren Projekt der Überblick über alle verwendeten Tests erhalten. Die einfachste Methode zur Erstellung einer Testsuite ist, in einer eigenen Datei alle benötigten Tests und das »Test::Unit«-Framework einzubinden.

require 'test/unit'
require 'test_case_1'
require 'test_suite_1'

Das Diagramm in Abbildung 1 veranschaulicht, wie eine solche Sammlung von Tests zusammenhängt.

Listing 1:
Fakultätsprogramm mit Test

01 # test_factorial.rb
02 require 'test/unit'
03 require 'factorial'
04 class TestFactorial < Test::Unit::TestCase
05    def test_calc
06       f = Factorial.new
07       assert_equal(120, f.calc(5) )
08       assert_equal(1, f.calc(1) )
09       assert_equal(1, f.calc(0) )
10       assert_nil(f.calc(1.3) )
11       assert_nil(f.calc(-1) )
12       assert_nil(f.calc("text"))
13    end
14 end
15 
16 # factorial.rb
17 class Factorial
18    def calc number
19       return nil if !number.is_a?(Integer) || number < 0
20       @result = 1
21       number.downto(1) { |i|
22          @result *= i
23       }
24       return @result
25    end
26 end

Listing 2:
»article.rb«

01 require 'net/http'
02 
03 class Article
04    def initialize
05       @host = 'www.linux-magazin.de'
06       @baseurl = '/Artikel/ausgabe/'
07    end
08 
09    def parse_articles(text)
10       ...
11    end
12 
13    def load_article_page(year, month)
14       ...
15    end
16 
17    def read_articles(year, month)
18       ...
19    end
20 
21    private
22    def createConnection
23       ...
24    end
25 
26    def get_address(year, month, suffix=nil)
27       ...
28    end
29 
30    def extract_article_text(text)
31       ...
32    end
33 
34    def extract_article_link(text, base)
35       ...
36    end
37 end
38 
39 ...

Zugriff auf private Methoden durch Vererbung

Das Testen der öffentlichen Methoden einer Klasse reicht in den meisten Fällen aus, denn es stellt dabei die korrekte Funktion der privaten Methoden sicher. Es gibt aber dennoch einen Weg, eine als »private« oder »protected« markierte Methode direkt zu testen: Man leitet von der zu testenden Klasse eine Subklasse ab, die geschützte Methoden als öffentlich definiert. Im folgenden Beispiel besitzt die »Klasse« eine geschützte (»protected«) Methode »geheim«. Die Unterklasse »Subklasse« besitzt eine gleichnamige öffentliche Methode, die mit »super« die geschützte Methode der Elternklasse aufruft:

class Klasse
   protected
   def geheim; end
end

class Subklasse < Klasse
   def geheim; super; end
end

Eine noch einfachere Möglichkeit bietet die Methode »send«, die auch geschützte Methoden aufrufen kann. Das Beispiel im obigen Code ist auch mit » Instanz .send :geheim« umsetzbar. Diese Fähigkeit der »send«-Methode ist zwar nicht dokumentiert, wird aber auch in Beispielen der Ruby-Dokumentation für ähnliche Zwecke verwendet. Optional lässt sich die Testsuite danach explizit als Klasse anlegen, bei der die Klassenmethode »suite« alle gewünschten Tests einbindet. Diese Klasse bekommt der Testrunner übergeben:

class Suite
   def self.suite
      suite = Test::Unit::TestSuite.new
      suite << TestCase1.suite
      suite << TestSuite1.suite
      return suite
   end
end
require 'test/unit/ui/gtk2/testrunner'
Test::Unit::UI::GTK2::TestRunner.run(Suite)

Listing 3 zeigt Beispiele zum Testen privater Methoden: So testet »test_get_address« die private Methode »get_address« direkt. Die erste Assertion der Testmethode »test_read_articles« überprüft dagegen, ob im weiteren Verlauf das Programm die richtige Adresse verwendet. Damit prüft sie indirekt gleichzeitig die korrekte Funktion der »get_address«-Methode.

Warum testen?

Aus welchen Gründen sollte ein Programmierer zusätzlich Zeit aufwenden, um für Tests ein zweites Programm ohne eigene Funktionalität parallel zum eigentlichen Programm zu schreiben?

Absicherung bei Änderungen am Programm

Programmierer verbringen oft einen überraschend kleinen Teil ihrer Zeit mit dem Schreiben des Programms, einen deutlich größeren mit der Fehlerbehebung. Dabei sind die meisten Fehler schnell ausgebessert - den Fehler finden kostet die meiste Zeit. Zu allem Überfluss können durch jede Änderung neue Fehler entstehen, gegen die sich Entwickler mit guten Unit-Tests absichern können.

Bei regelmäßiger Ausführung der Tests ist der Fehler sehr schnell gefunden, weil nur der Code, der seit dem letzten Testdurchlauf hinzugekommen ist, überhaupt zur Debatte steht. Das ist besonders für das Refactoring wichtig, also für Änderungen am Programm, um dessen Struktur (nicht dessen Funktionalität) zu verbessern.

Unit-Tests sind zwar kein Allheilmittel gegen Programmfehler, können aber dabei helfen, deutlich weniger Arbeitszeit mit der Fehlerbehebung zu verbringen.

Ausnahmesituationen und Grenzfälle

Unit-Tests eignen sich dazu, alle Arten von Ausnahmesituationen und Grenzfällen zu kontrollieren. In diesen Bereichen verstecken sich sehr häufig Fehler, die in einem nicht getesteten Programm selten auftreten und dadurch schwierig zu eliminieren sind.

Dokumentation des Programms

Unit-Tests dokumentieren ein Programm, indem sie mit dem Interface der getesteten Klasse kommunizieren und die erwarteten Rückgabewerte festlegen.

Test Driven Development

Beim Test Driven Development (TDD) werden die Tests vor der Implementation des Programms erstellt. Das Programm gilt als fertig gestellt, sobald alle Tests ohne Fehler absolviert werden. Beim Schreiben eines Tests muss sich der Programmierer Gedanken über die Funktionsweise einer Klasse machen. Der Fokus liegt dabei automatisch auf dem Interface und nicht auf der Implementation - eine der wichtigsten Richtlinien für objektorientiertes Programmieren. Das führt meist zu übersichtlicherem Code. Entwickler, die zuerst die Tests schreiben, versetzen sich in den Klassenanwender und werden das Interface (hoffentlich) verständlicher aufbauen.

Dokumentation der Fehler

Tritt ein Fehler auf, der nicht durch einen Test abgedeckt ist, schreibt der Entwickler einen Test, der diesen Fehler dokumentiert. Sobald der Test fehlerfrei durchgeführt wird, ist der Bug behoben. So ist auch sichergestellt, dass derselbe Fehler kein zweites Mal unbemerkt durch die Tests rutscht.

Diesen Artikel druckenDiesen Artikel weiterempfehlen Diesen Artikel kommentieren Newsletter abonnieren
Share/Bookmark
Ähnliche Artikel
Babylon zu fünft Populäre Programmiersprachen treten gegeneinander an
Geistertanz Die Skriptsprache Boo
Babylon zu fünft Populäre Programmiersprachen treten gegeneinander an
PC statt Bunsenbrenner Chemoinformatik mit dem Chemical Development Kit
Code-Vehikel Alternativen zu den populären Programmiersprachen
Alternativ-Baukästen GUI-Programmierung mit Fltk, Wx-Widgets und EFL
Whitepaper
Open Source Datenintegration in der Praxis: Fallstudien und Anwendungsbeispiele (Folge 2)

Der zweite Teil des Open Source Datenintegration in der Praxis: Fallstudien und Anwendungsbeispiele White Papers beleuchtet anhand weiterer ausgewählter Case Studies die Implementierung von Open Source Datenintegration in der Praxis und benennt die daraus resultierenden Vorteile.

Download PDF (Registrierung erforderlich)
Usage Landscape Enterprise Open Source Data Integration

Die Nachfrage nach Datenintegrationslösungen für Unternehmen ist zunehmend gestiegen und vor allem das Interesse an Open Source Technologien wird immer größer. Doch wie und von wem werden Open Source Datenintegrationslösungen genutzt und welches Nutzungsverhalten lässt sich daraus ableiten? Das vorliegende White Paper präsentiert die Erfahrungswerte von über 1000 Open Source Nutzern und liefert fundierte Antworten auf diese Fragen.

Download PDF (Registrierung erforderlich)
Kommentare (0)