👋 Nový obsah na borekb.cz

Info Tento blog je v "read-only módu" a nový obsah již nebude přibývat. O vývoji píšu na DevBlog.

TDD - první zkušenosti

Dnes poprvé jsem zkoušel vyvíjet metodikou TDD (Test-Driven Development) a spíš jen pro zajímavost si chci poznamenat své první pocity.

K TDD jsem měl doposud poněkud zvláštní vztah – hodně jsem o tom přečetl, o přínosech jsem celkem pevně přesvědčen, ale přesto jsem nikdy nedospěl do bodu, kdy bych TDD začal aktivně praktikovat. Většinou to bylo proto, že všechny mé projekty byly v pokročilé fázi svého životního cyklu a za tohoto stavu se mi s TDD začínat nechtělo. Teď se ale objevil nový projekt, takže jsem měl k vyzkoušení TDD ideální příležitost!

Trochu paradoxní je, že zatímco „chuť na TDD“ jsem dostal především čtením zdrojů od pokročilých .NET vývojářů, můj současný projekt staví na PHP, což historicky rozhodně není svět „best practices“ (přísnějším hodnocením obsahujícím slova „špagety“, „bastlení“ a podobně nechci urazit stále narůstající počet PHP vývojářů, kteří se o kvalitu kódu přinejmenším snaží). Nicméně i ve světě PHP pro unit testy nějaké ty frameworky existují, takže po technologické stránce problém nebyl. Já jsem si vybral SimpleTest, protože vypadal, že má vše, co budu pro začátek potřebovat.

Nastal tedy čas napsat svůj první test! Vytvářím jednoduchou REST-style webovou službu a v pravém duchu TDD jsem nesměl udělat nic, na co bych už předem neměl napsaný test, proto prvním krokem bylo vytvoření následujícího testu:

public function test_Propowners_Resource_Exists() {
  $client = new Zend_Http_Client($this->BASE_URL);
  $response = $client->request();
  $this->assertTrue($response->isSuccessful());
}

Protože tento test byl tím úplně prvním, co jsem udělal, musel selhat ($this->BASE_URL mířilo na soubor propowners.php, který zatím neexistoval). Spuštění testu v prohlížeči přineslo očekávaný výsledek:

Test neprošel

Přidání prázdného souboru propowners.php přineslo kýžený výsledek:

Test prošel, hurá!

Popravdě, ještě nikdy jsem neměl takovou radost z vytvoření prázdného souboru :)

V tomto způsobu práce jsem pokračoval i nadále. Můj další test testoval, jestli prostý požadavek na propowners.php vrátí jednoduchý textový popis webové služby. Prázdný soubor samozřejmě nic takového nevrací, proto jsem dostal očekávanou červenou. echo „About this web service: …“ splnilo základní požadavek a test se zazelenal. Juchú! Další test zkoumal, jestli požadavek s nějakým HTTP GET parametrem vrací MIME type text/xml… a tak dále, a tak dále. TDD-il jsem, jen to svištělo :)

Moje první dojmy z reálného používání TDD:

  • V principu je to hrozně jednoduché. Zatím se nepotýkám s integračními testy, nepotřebuju mockovat databázi a podobně, takže jde všechno hladce a bez problémů.
  • 50% času mi trvalo zprovoznění a nastudování třídy Zend_Http_Client, 40% času jsem se potýkal s OOP syntaxí PHP, kterou už jsem trochu pozapomněl, a pouhých 10% času mi zabralo psaní testu jako takového. Tyto poměry se určitě změní, ale přeci jen to poukazuje na zajímavou věc: v normálním přístupu bych prostě začal mydlit propowners.php a o nějakou simulaci HTTP requestů pomocí Zend_Http_Client bych se vůbec nemusel starat, testy mě donutily rozšířit obzory a zapojit něco, co by jinak v nikde v kódu vůbec nebylo. Tady pravděpodobně leží to pravé zdržení způsobené TDD, ne fakt psaní testů sám o sobě.
  • Vidět zelenou je krásný pocit. Dodává mi sebevědomí, že napsaný kód je možná i správný :)
  • Zatím musím narazit na situaci, kdy změna produkčního kódu způsobí selhání některého testu a naznačí tak, že jsem něco zkazil. Hádám, že tady se nejlíp projeví hodnota existujících unit testů.
  • Zatím pracuji zásadně s testovacím souborem, ten reálný jsem vůbec nemusel spustit a přesto si jsem celkem jistý, že je implementovaný správně. To považuji za pěknou výhodu.

Uvidím, jak to půjde dál, ale zatím se mi TDD zamlouvá. A ani to moc nebolelo :)

Zařazeno do kategorií
Aleš Roubíček (Pá, 2008-06-27 08:13):

Tož přeju hodně štěstí při prvních složitějších věcech :) Skutečné TDD jsem si zkusil jen jednou a měl jsem podobnou radost, jak to všechno přibejvá. Kupodivu tam skutečně chyby nebyly, ale nejspíš jsem zabředl do přespříli specifikovaných testů… :)

Borek (Pá, 2008-06-27 10:18):

Jo jo, taky jsem zvědav, jak to půjde se složitějšími věcmi. Mám pár dalších poznatků, které asi budou stát za blognutí, takže tento článek o TDD rozhodně není poslední :)

Filda (Pá, 2008-06-27 10:07):

Ještě by bylo hezký kdyby si napsal jak testy spouštíš.

Borek (Pá, 2008-06-27 10:27):

SimpleTest má několik možností spuštění. Asi nejjednodušší je napsat naprosto běžnou PHP třídu a jen na začátek přidat require_once ‚simpletest/au­torun.php‘ – soubor obsahující test pak stačí načíst v prohlížeči.

Jak jsem ale tvořil víc a víc testů v různých souborech, stal se tento přístup málo pružným. Nakonec jsem si napsal jednoduchý skript, který projde adresář „tests“ a najde všechny soubory končící na „Test.php“ a zahrne je to testovací suity. Vypadá to nějak takto: (ještě jsem přidal možnost určit testy explicitně pomocí parametru v query stringu, takže můžu zavolat něco jako testrunner.php?t=Ob­jectUtilTest,Ga­tewayTest)

<?php
require_once '../lib/simpletest/unit_tester.php';
require_once '../lib/simpletest/reporter.php';

$tests = new TestSuite("Property Owners web service test suite");

if (isset($_GET['t'])) {

  // In the 't' parameter, there can be a comma-delimited list of tests to run.
  // Examples:
  //   t=ObjectUtilTest
  //   t=ObjectUtilTest,ResponseBuilderTest
  //   t=ObjectUtil,ResponseBuilder    <-- "Test" suffix is optional

  $tests_to_run = explode(",", $_GET['t']);
  foreach ($tests_to_run as $test) {
    if (file_exists($test . "Test.php")) {
      $filename = $test . "Test.php";
    } else {
      $filename = $test . ".php";
    }

    $tests->addFile($filename);
  }


} else {

  $files = scandir(".");
  foreach ($files as $file) {
    if (strpos($file, "Test.php") !== false) {
      $tests->addFile($file);
    }
  }

}
$tests->run(new HtmlReporter());

?>
Filda (Pá, 2008-06-27 14:29):

To je vcelku rozumný. Mě zajímalo jestli třeba nepoužíváš nějakou integraci do IDE. Například simpletest má jakoustakous podporu pro Eclipse. Já osobně se snažím používat PHPUnit, ale přijde mi, že se ty testy obecně (mluvím o php) pouštěj dost nepohodlně.

Borek (Pá, 2008-06-27 16:16):

Ano, integraci s Eclipse/PDT jsem zkoušel, ale protože třeba hned můj první test byl vlastně integrační a ne unit test (vyvolání HTTP requestu), běh v prohlížeči se mi zrovna hodil do krámu.

O PHPUnit mi ani nemluv. Dneska jsem se hodně dlouho snažil rozchodit PHPUnit ve vývojové verzi 3.3 (má asserty na porovnávání dvou XML struktur, což bych zrovna potřeboval), ale po několika hodinách jsem to vzdal. Instalace přes PEAR (nemám rád), nepohodlné spouštění testů z příkazové řádky, žádný spolehlivě fungující HTML runner atd. – to vše mě celkem odradilo. No, PHPUnit mě pořád láká (z pohledu možností se mi líbí víc než SimpleTest), ale ten nástroj mě prostě zatím víc zdržuje než pomáhá.

Borek (Pá, 2008-06-27 16:18):

Mimochodem, má PHPUnit nějakou integraci s PDT? Četl jsem, že existuje plugin do Zend Studia, ale na své občasné avantýry s PHP mi PDT bohatě stačí a za profi nástroj se mi platit nechce.

Filda (Pá, 2008-06-27 18:32):

Vidim, že máme s phpunit stejný zkušenosti. Se simpletest jsem si hrál někdy před dvěma lety a v poslední době mi právě přišlo, že phpunit udělalo velkej krok dobředu tak jsem ho chtěl vyzkoušet. Ale je to přesně jak řikáš. A co se týče integrace do IDE tak o ničem jiným než o Zend studiu nevim.

Borek (Pá, 2008-06-27 21:26):

Vedu teď poměrně dlouhou konverzaci s autorem PHPUnit a ten si celkem pevně stojí za tím, že používat PHPUnit je hračka a nic jiného než command-line není potřeba. To si má člověk všechno programovat sám? :)

LLook (So, 2008-06-28 15:44):

TDD svoje výhody nejvíc předvede při změnách. Když změním rozhranní nějakého objektu, musím najít a upravit také všechna místa, kde se tento objekt používá. Jak se kód rozrůstá, tak je bez spolehlivých testů čím dál obtížnější zanášet změny a zároveň se rozrůstá i potřeba změn.

Někdy dá pěknou fušku vymyslet test, ale po zkušenostech musím říct, že to za to stojí.

Aleš Roubíček (Po, 2008-06-30 05:24):

TDD není o vymýšlení testů, ale o návrhu aplikace. To že je kód pokrytý testy by mělo být pouze jako bonus.

Proto abys měl aplikaci pod testy není řeba TDD, ale klidně můžeš psát testy později, dosáhneš v tomhle směru přinejmenším stejného výsledu.

Botanicus (Ne, 2008-06-29 21:02):

Jo, TDD je suprova vec :) Osobne mam teda radsi BDD, ale to uz neni tak podstatne, podstatne je testovat. Ja testuju de facto vsechno a zpravidla mam radek testu vyrazne (az nekolikanasobne) prevysujici pocet radku produkcniho kodu. Nejlepsi je, ze uz se nebojim zasahovat i do aplikaci, u nichz jsem vypustil z pameti jak vlastne funguji…

Komentáře jsou uzavřeny (blog je v read-only módu). Pokud mě chcete kontaktovat, můžete mailem.