|
 |
 |
 |
 |
 |
 |
 |
 |
 |
(15.08.04 20:08)
Unit-Tests sind etwas Wunderbares. Sie sind das Netz, um die kleinen Fehlerfische in unseren Programmen zu erwischen, bevor sie zu menschenverschlingenden Haien werden. Das erspart uns eine Menge Ärger und Probleme. Doch manchmal kommt es vor, dass ein Test selber zum Problem wird, z.B. wenn wir bezüglich seines Zwecks im Dunkeln tappen, weil der Testname uns nichts verrät über das, was da getestet wird. So geschehen im folgenden Beispiel:
|
 |
 |
class TestIfBlock < Test::Unit::TestCase
def setup
@conditional = BlockConditional.new {
@condition
}
@trueSequence = SequenceFake.new(0,
"sequence1")
@falseSequence = SequenceFake.new(0,
"sequence2")
end
def test_twoPassesOneCall
ifBlock = IfBlock.new(@trueSequence,
@falseSequence,
@conditional)
# true branch
@condition = true
assert ifBlock.execute.done?
assert_equal 1, @conditional.evalCount
assert_equal 1, @trueSequence.executeCount
assert_equal 0, @falseSequence.executeCount
# false branch
@condition = false
assert ifBlock.execute.done?
assert_equal 2, @conditional.evalCount
assert_equal 1, @trueSequence.executeCount
assert_equal 1, @falseSequence.executeCount
end
|
|
 |
 |
Der Test ist glücklicherweise sehr kurz. Dennoch habe ich einen Moment gebraucht, um herauszufinden, was hier getestet wird. Dazu musste ich mir ansehen, wie der Test abläuft. Das Traurige: Den Test habe ich zusammen mit meinem Pair-Programming-Partner vor knapp einer Woche geschrieben. Wieder einmal ein anschaulicher Beweis dafür, wie schnell man vergisst und wie wichtig gute Namen sind!
Übrigens, ein guter Test, um zu überprüfen, ob der Testname gut gewählt ist, besteht darin, einem anderen Entwickler die CUT in wenigen Worten zu erklären und ihm dann den gewählten Testnamen zu nennen. Wenn er darauf hin erklären kann, was der Test überprüft, taugt der Name. Hat der Entwickler dagegen Mühe, den Testzweck in wenigen Worten zu erklären, dann ist der Name meist schlecht gewählt. Ich bin mir ziemlich sicher, dass unser ursprünglicher Testname dieser Überprüfung nicht stand gehalten hätte.
Im vorliegenden Beispiel geht es um die Klasse IfBlock. Diese repräsentiert eine logische Fallunterscheidung als Teil einer block-orientierten Domänensprache. Ein IfBlock verfügt über eine logische Bedingung, deren Auswertung zur Ausführung des entsprechenden Wahr- oder Falsch-Zweiges führt, der jeweils eine Verbundanweisung verkörpert. Der Block wird über execute ausgeführt. Aufgrund von Anforderungsdetails, die hier nicht von Interesse sind, kann es vorkommen, dass die jeweilige Verbundanweisung nicht vollständig ausgeführt werden kann, und der IfBlock daher erneut ausgeführt werden muss, um den vorgängig gewählten Zweig abzuarbeiten. In diesem Fall darf die Bedingung natürlich nicht erneut ausgewertet werden. Erst wenn die gewählte Verbundanweisung vollständig abgearbeitet wurde, muss die erneute Ausführung des IfBlocks die Bedingung wieder auswerten.
Zurück zum Namen des Tests : Er drückt eher aus, wie getestet wird, gibt einem aber keinen Hinweis auf den eigentlichen Zweck – das Was - des Tests. Wahrlich kein Beispiel guter Namensgebung. Im Nachhinein kann ich dem ursprünglichen Namen nichts Gutes mehr abgewinnen (*). Der Umstand, dass ich alleine aufgrund des Testnamens nicht mehr verstehe, was der Tests tut, zwingt mich dazu, einen besseren Namen zu finden.
Was also wird hier nun getestet? Der Tests dreht sich um die oben ausgeführte Anforderung, dass ein IfBlock, nachdem er vollständig abgearbeitet wurde, bei einer erneuten Ausführung die Bedingung wieder auswertet, um den auszuführenden Wahr- oder Falsch-Zweig zu bestimmen. Ich habe daher den folgenden Namen gewählt, der diesen Umstand zum Ausdruck bringen soll:
|
 |
 |
test_ifExecutionOfBlockThatIsDone
EvaluatesConditionAgain
|
|
 |
 |
Wow, was für ein langer Name, mögen ein paar von Ihnen jetzt sagen. Da schreibe ich mir ja die Finger wund. Schon richtig, aber das Schreiben des Namens ist hier wirklich nicht das Problem. Es geht nämlich nur darum, einen Testnamen zu haben, der die Absicht des Tests klar zum Ausdruck bringt, damit uns das Lesen und Verstehen leichter fällt; das ist wie mit unserer Sprache: das Ziel einer Grammatik ist es nicht, dem Schreiber das Schreiben zu erleichtern sondern dem Leser das Lesen.
Wenn wir uns den Test noch einmal genauer anschauen, so sehen wir, dass wir streng genommen zwei Dinge testen: Zum einen wird das getestet, was der Name sagt, nämlich, ob die wiederholte Ausführung eines IfBlocks, nachdem eine vorherige Ausführung abgeschlossen wurde, die Bedingung des Blocks erneut auswertet, um die auszuführende Verbundanweisung zu bestimmten. Zum anderen überprüfen wir aber auch, ob die Bedingung grundsätzlich ausgewertet und zur korrekten Bestimmung des auszuführenden Zweiges führt. Auf diesen Punkt werde ich in einem späteren Beitrag noch einmal zurückkommen.
Für heute will ich es mit der Erkenntnis bewenden lassen, dass gute Namen für Testfälle das A und O sind. Und das ist auch nicht weiter erstaunlich, denn letztlich trifft diese Aussage auf alle Abstraktionen in einem System zu. Man sollte darum immer sorgfältig über Namen nachdenken und jederzeit bereit sein, sie zu ändern, wenn sie sich als problematisch erweisen.
-nemo :-)
|
 |
 |
(*) Während ich das niederschreibe, dämmert mir langsam wieder, was der ursprüngliche Name ausdrücken sollte: Der IfBlock wird zweimal ausgeführt (twoPasses), und die Ausführung der jeweiligen Verbundanweisung ist jeweils bereits nach dem ersten execute abgeschlossen (oneCall). Aber eben, der resultierende Name beschreibt im besten Fall, wie und nicht was getestet wird.
Über diesen Artikel diskutieren.
|
 |
 |
Zugriffe heute: 1 - gesamt: 308.
|
 |
|