(edit)
Hide minor edits - Show changes to markup
(:redirect 'http://docs.studip.de/develop/Entwickler/HowToUnitTest':)
< Templates? | Entwicklungs-HOWTO
Als Beispiel werden wir einen kleinen Test für die Funktion words schreiben, die sich in der Datei lib/functions.php befindet.
words
lib/functions.php
Als Beispiel werden wir einen kleinen Test für die Funktion #words schreiben, die sich in der Datei lib/functions.php befindet.
#words
Ausserdem erkennt man gut das four phase test pattern. Die Kommentare trennen die 4 Phasen eines Tests. Unter "fixture" versteht man die Ausgangssituation, die geschaffen werden muss, um den Test auszuführen. In unserem sehr einfachen Beispiel ist dafür nichts notwendig. In der "exercise"-Phase wird dannn das zu testende Verhalten herbeigeführt, was in diesem Fall dem Aufruf der Funktion #words entspricht. Danach wird überprüft, ob das Ergebnis des Verhaltens den Erwartungen entspricht. Und ganz am Ende räumt man alles ab, damit der nächste Test in einer klar definierten Umgebung ablaufen kann. Bei unserem Test ist dafür nichts zu tun.
Ausserdem erkennt man gut das four phase test pattern. Die Kommentare trennen die 4 Phasen eines Tests. Unter "fixture" versteht man die Ausgangssituation, die geschaffen werden muss, um den Test auszuführen. In unserem sehr einfachen Beispiel ist dafür nichts notwendig. In der "exercise"-Phase wird dannn das zu testende Verhalten herbeigeführt, was in diesem Fall dem Aufruf der Funktion #words entspricht. Danach wird überprüft, ob das Ergebnis des Verhaltens den Erwartungen entspricht. Und ganz am Ende räumt man alles ab, damit der nächste Test in einer klar definierten Umgebung ablaufen kann. Bei unserem Test ist dafür nichts zu tun. Auf die Phasen 1 und 4 (Fixture-Setup und -Teardown) wird weiter unten vertieft eingegangen.
TODO (mlunzena) fehlt noch
TODO (mlunzena) Hier fehlen noch ein paar weitere Links.
function test_something() {
function testSomething() {
function testWords() {
$x
true
false
$t
$x == $y
$y
$margin
$x === $y
$p
$e
$string = "one two three"; $expected = array('one', 'two', 'three'); $outcome = words($string); $this->assertEqual(, $expected);
// setup fixture // exercise system under test $words = words("one two three"); // validate outcome $this->assertEqual($words, array('one', 'two', 'three')) // teardown fixture
Es wird also einfach ein String mit drei Wörtern angelegt, dann an #words verfüttert, und zum Schluss mit Hilfe der Methode #assertEqual verglichen. Wenn diese Assertion erfolgreich ist, wird dieser Test wiederum erfolgreich sein.
#assertEqual
''The smallest unit of a…er…unit test is the assertion. Here we want to assert that the log file we just sent a message to was indeed created. UnitTestCase::assertTrue() will send a pass event if the condition evaluates to true and a fail event otherwise. We can have a variety of different assertions and even more if we extend our base test cases.
Here is the base list… assertTrue($x) Fail unless $x evaluates true assertFalse($x) Fail unless $x evaluates false assertNull($x) Fail unless $x is not set assertNotNull($x) Fail unless $x is set to something assertIsA($x, $t) Fail unless $x is the class or type $t assertNotA($x, $t) Fail unless $x is not the class or type $t assertEqual($x, $y) Fail unless $x == $y is true assertNotEqual($x, $y) Fail unless $x == $y is false assertWithinMargin($x, $y, $margin) Fail unless $x and $y are separated less than $margin assertOutsideMargin($x, $y, $margin) Fail unless $x and $y are sufficiently different assertIdentical($x, $y) Fail unless $x === $y for variables, $x == $y for objects of the same type assertNotIdentical($x, $y) Fail unless $x === $y is false, or two objects are unequal or different types assertReference($x, $y) Fail unless $x and $y are the same variable assertCopy($x, $y) Fail unless $x and $y are the different in any way assertSame($x, $y) Fail unless $x and $y are the same objects assertClone($x, $y) Fail unless $x and $y are identical, but separate objects assertPattern($p, $x) Fail unless the regex $p matches $x assertNoPattern($p, $x) Fail if the regex $p matches $x expectError($e) Triggers a fail if this error does not happen before the end of the test expectException($e) Triggers a fail if this exception is not thrown before the end of the test''
In diesem Test wird ein String mit drei Wörtern angelegt, dann an #words verfüttert, und zum Schluss mit Hilfe der Methode #assertEqual verglichen. Ist diese Assertion wahr, ist der Test erfolgreich.
Die kleinste Einheit eines Unit-Tests ist die Assertion. Im Beispiel wollten wir sicherstellen, dass die Funktion tatsächlich das erwartete Array zurückliefert. UnitTestCase#assertEqual ist erfolgreich, wenn der erste Parameter == dem zweiten Parameter ist.
UnitTestCase#assertEqual
Es gibt eine noch viele weitere Assertions und selbstverständlich kann der Programmierer jederzeit durch Erweiterung der Klasse UnitTestCase neue hinzufügen.
UnitTestCase
Die grundsätzlichen Assertions werden in der folgenden Tabelle aufgelistet:
'' The smallest unit of a…er…unit test is the assertion. Here we want to assert that the log file we just sent a message to was indeed created. UnitTestCase::assertTrue() will send a pass event if the condition evaluates to true and a fail event otherwise. We can have a variety of different assertions and even more if we extend our base test cases.
expectException($e) Triggers a fail if this exception is not thrown before the end of the test ''
expectException($e) Triggers a fail if this exception is not thrown before the end of the test''
$this->assertEqual(words($string), $expected);
$outcome = words($string); $this->assertEqual(, $expected);
#assertEqual überprüft, ob beide Argument gleich sind. Dabei wird der einfache Vergleich mit == verwendet.
==
Here is the base list… assertTrue($x) Fail unless $x evaluates true assertFalse($x) Fail unless $x evaluates false assertNull($x) Fail unless $x is not set assertNotNull($x) Fail unless $x is set to something assertIsA($x, $t) Fail unless $x is the class or type $t assertNotA($x, $t) Fail unless $x is not the class or type $t assertEqual($x, $y) Fail unless $x == $y is true assertNotEqual($x, $y) Fail unless $x == $y is false assertWithinMargin($x, $y, $margin) Fail unless $x and $y are separated less than $margin assertOutsideMargin($x, $y, $margin) Fail unless $x and $y are sufficiently different assertIdentical($x, $y) Fail unless $x === $y for variables, $x == $y for objects of the same type assertNotIdentical($x, $y) Fail unless $x === $y is false, or two objects are unequal or different types assertReference($x, $y) Fail unless $x and $y are the same variable assertCopy($x, $y) Fail unless $x and $y are the different in any way assertSame($x, $y) Fail unless $x and $y are the same objects assertClone($x, $y) Fail unless $x and $y are identical, but separate objects assertPattern($p, $x) Fail unless the regex $p matches $x assertNoPattern($p, $x) Fail if the regex $p matches $x expectError($e) Triggers a fail if this error does not happen before the end of the test expectException($e) Triggers a fail if this exception is not thrown before the end of the test ''
$this->assertEqual(words($string), array('one', 'two', 'three'));
$expected = array('one', 'two', 'three'); $this->assertEqual(words($string), $expected);
Normalerweise würde man mehr Test-Methoden in einem TestCase haben, aber das kann ja noch kommen.
Assertions in den Test-Methoden führen zu Meldungen im Test-Framework, welches diese je nach Erfolg oder Mißerfolg anzeigt.
In unserem ersten Test wollen wir den happy path der Funktion #words testen. Schauen wir uns noch einmal diese Funktion an:
/** * Splits a string by space characters and returns these words as an array. * * @param string the string to split * * @return array the words of the string as array */ function words($string) { return preg_split('/ /', $string, -1, PREG_SPLIT_NO_EMPTY); }
Diese Funktion erlaubt es also offenbar, aus einem String ein Array zu machen, das die Wörter des Strings als Elemente hat.
Damit sieht der erste Test so aus:
[...] class FunctionsTest extends UnitTestCase { function testWords() { $string = "one two three"; $this->assertEqual(words($string), array('one', 'two', 'three')); } }
Man halt also eine Klasse, die von UnitTestCase abstammt, und die Test-Methoden enthält, deren Name mit test beginnt. Der Name sollte andeuten, was man eigentlich gerade testet. Er darf gerne lang sein. Niemand wird diese Methoden per Hand aufrufen müssen.
test
…
function test_something() { … }
Jeder Unit-Test sieht minimal so aus:
<?php /* * Copyright (C) 2010 - Marcus Lunzenauer <mlunzena@uos.de> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. */ // make sure that the code under test is available require_once 'lib/functions.php'; class FunctionsTest extends UnitTestCase { }
Der Unit-Test-Runner, den wir oben ausführten, findet Test-Dateien in den Verzeichnissen test/lib und test/lib/classes. Dabei müssen die Namen dieser Dateien auf test.php enden.
test/lib
test/lib/classes
test.php
In unserem Beispiel legen wir also eine Datei test/lib/functions_test.php an. Per Konvention sollen Tests, die sich auf Code in lib beziehen, im Verzeichnis test/lib liegen, und Tests, die sich auf Klassen aus lib/classes beziehen, im Verzeichnis test/lib/classes.
test/lib/functions_test.php
lib
lib/classes
TODO (mlunzena) Was sind Unit-Tests? Wofür sind sie gut? Welches Tool wird verwendet?
Daraufhin erhält man als Ausgabe die Zahl der ausgeführten und erfolgreichen Tests. Das sieht dann z.B. so aus:
Dieses Kommando durchsucht das Test-Verzeichnis nach Unit-Tests und führt diese aus. Daraufhin erhält man als Ausgabe die Zahl der ausgeführten und erfolgreichen Tests. Das sieht dann z.B. so aus:
@]
Ein Fehler würde in etwa so aussehen:
[@ All tests 1) at [/test/lib/classes/assets_class_test.php line 43]
in test_class_should_exist in AssetsTestCase
FAILURES!!! Test cases run: 10/10, Passes: 185, Failures: 1, Exceptions: 0
Rufe in deiner Stud.IP-Working-Copy folgendes Kommando im Terminal auf:
php test/all_tests.php
All tests OK Test cases run: 10/10, Passes: 186, Failures: 0, Exceptions: 0
Source: Basis-Wiki-Hilfe | Last change: April 01, 2011, at 11:19 PM, tthelen | Local view: Basis-Hilfe