Jaké používám testovací nástroje?
Tento článek byl původně publikován Patem Eylerem na téma testovací nástroje. Znovu byl publikován v síti Rubilation a je dostupný v dalších jazycích.
Tento článek se podrobně zabývá nástroji, které se používají při vývoji počítačových aplikací metodou "Test-first development" (nejprve testuj). Pokud nevíte co tento pojem znamená a chtěli by jste se dozvědět více, zde je několik dobrých zdrojů, kde začít:
- XP Magazine
- můj článek (pp. původního autora) at IBM Developerworks
- můj dřívější článek o ZenTest-u (pp. původního autora)
Rád pišu testy dříve než-li normální kód, protože se pak cítím více sebejistý tím co jsem napsal.
Můžu sestavit fungující implementaci rychleji a můžu se soustředit na refaktorizaci a návrh lepšího kódu. Opravdu to nebolí, protože Ruby poskytuje kvalitní nástroje, které umožňují princip Test-First velice jednoduše uplaťnovat.
Nástroje, které používám pro Test-First programování zahrnují Test::Unit, rake, rcov, autotest a unit_diff. První dva jsou povědomé většině Ruby hackrů.
Pokud jste o těchto nástrojích ještě neslyšeli, Test::Unit je velice dobře zdokumentován v knížce Pick Axe a v dokumentaci www.ruby-doc.org.
O nástroji rake si můžete více přečíst v mém článku na IBM Developerworks a v článku od Martina Fowlera.
Pokud jste ještě nikdy nepracovali s Test::Unit ani rake, vyhraďte si čas na to naučit se používat tyto excelentní nástroje. Test::Unit je distribuováno s Ruby a rake je dostupný jako Ruby balíček (gem) na RubyForge.
Jako další nástroje v mém toolboxu, rcov používám jako analytický nástroj pokrytí mého kódu testy.
Když tento nástroj spustíte nad svými Unit testy, vygeneruje pro vás analyzu pokrytí kódu jednolivými testy. rcov je dostupný na adrese eigenclass.org. Můžete vygenerovat HTML výstup (podívejte se na příklad zde) nebo můžete generovat ASCII výstup, což je zkrácený výpis viz. níže.
rcov pracuje velice rychle. Běh programu s rcov je jenom 2x až 3x pomalejší než normální běh programu. Jako přívdavek rcov produkuje pěkné a užitečné výsledky.
class MockDB | 2
| 0
def exec(query, &block) | 11
case query.split(' ')[3] | 5
when 'zero' | 5
num = 0 | 1
when 'one' | 4
num = 1 | 1
else | 0
num = 2 | 3
end | 0
| 0
yield [num] | 5
| 0
end | 0
| 0
end | 0
rcov spuštíte proti své sadě testů takto:
$ rcov test/test_hostnameZajímavé možnosti příkazového řádku zahrnují:
- -t: generuj čistě textový výstup
- -T: generuj pěknější textový výstup
- -p: generuj "profiling" výstup
- -x: vyřaď soubory; tento očekává čárkou oddělený seznam regexů
- --no-html: nevytvářej HTML soubory
Pokud si zvolíte generování textového výstupu může být užitečné nechat výstup přesměrovat do souboru. Nebo si můžete udělat čaj a počkat než vám rcov vygeneruje celý výstup. Výstup může být celkem dlouhý. Ačkoli se nástroje pokrytí normálně používají k názorné ukázce, kde je potřeba napsát více testu, mám zkušenost že mi rcov umožňuje zvážit refaktorizaci kódu. Kdysi jsem psal kód, který testoval hostname, tak že jsem nejprve psal testy několik hodin. Pak jsem se rozhodl, že si udělám přestávku od psaní kódu a podívám se jak dobře jsem testy pokryl kód. Očekával jsem, že to bude na 100%, ale někdy je dobré mít důkaz. A proto jsem byl šokován, že na konci zeleného sloupce byl červený. Něco prostě nebylo otestováno. Prošel jsem si kód a moje asserty. Viděl jsem, že jsem testoval případ neúspěchu, ale rcov mi prostě nevěřil. Nakonec jsem zjistil, že zkouška, kterou jsem naimplementoval se neprojevila, kvůli testu, který před tímto testem tuto chybu odchytil. Což zapřičínilo, že chyba byla zachycena dříve než se testovací kód dostal k mému testu. Později jsem musel tento testovací kód rozdělit do 2 metod nebo eliminovat špatný kód. Díky rcov byl můj kód nakonec menší.
Aktuální release rcov-u obsahuje malou chybku (bug), na kterou musíte dávat pozor. rcov se nedívá na řádky pokračující "and" nebo "or" výrazy. V dalším release bude tato chyba odstraněna.
Další nástroje, které používám jsou autotest a unit_diff, které jsou distribuovány s ZenTest, který je dostupný jako rubygem. Oba jsou určeny pomoci vám lépe zahrnout plynulé testování do vaší denní rutiny. S vaším kódem v ./lib a vašími testy v ./test, autotest postupně jednotlivé testovací soubory spustí a zobrazí výsledky. Vytváří mapu mezi testy a vaším implementací tříd a metod. Pokaždé, když uložíte soubor v mapě nebo vytvoříte soubor, který se ocitne na seznamu, spustí se testy. Pokaždé, když test selže, autotest bude spouštět jenom tyto neúspěšné testy. Toto vám umožňuje skoro okamžitě zachytit neúspěšné testy a zaměřit se na jejich opravu.
Zabralo to několik hodin než jsem si navyknul s autotestem pracovat a dostat se do rytmu práce. První věc, kterou jsem s autotestem udělal byly malé úkoly (updatoval jsem své testy pro Ruby 1.8.4), takže autotest neměl čas udělat cyklus spánku , proskenovat změny a spustit testovací cyklus. autotest má způsob jak se s tímto vypořádat, nicméně
zmáčknutím kombinací kláves (jednou) Ctrl-c znovuspustí okamžitě testy. Zmáčknutím kombinace dvakrát způsobí ukončení autotestu. Také jsem zjistil, že autotest nemá rád, pokud test nespěje. Když píšu kód způsobem Test-First od začátku vývojového cyklu, většinou potřebuji soubor, který ještě neexistuje. Během normálního vývoje jsem náchylný generovat překlepy nebo syntaktické chyby.
Jakákoliv z těchto chyb vyústí v chybu vygenerovanou Ruby a zjistím, že na řádce se píše něco jako:
# Test::Unit died, you did a really bad thing, retrying in 10Není to zrovna nejlepší věc pro vaše ego, ale je to dostatečně výřečný způsob jak důrazně varovat a udržet vás na správné cestě.
unit_diff je další malý prográmek, který se rychle stane vaším neocenitelným pomocníkem. Pomáhá vám "očkávané" a "získané" části výstupu z neúspěšných assertů prohnat diff-em, a zredukuje tak velké množství textu na něco mnohem více zvládnutelného.
Eric Hodel autor unit_diff-u říká, že program byl napsán aby nápomohl vývoji ParseTree, kde každý neúspěšný assert mohl produkovat několik obrazovek,
nepochopitelného, těžko čitelného výstupu. unit_diff obrátil tuto noční můru do dvou nebo tří řádku, které ukazují na konkrétní chybu. Navíc jsem zjistil, že je tento nástroj neocenitelný a velice jednoduše použitelný při práci s XML výstupem. Jednoduše takto:
$ ruby test/test_hostname |unit_diffKaždá nalezená chyba je chycena a vhodně zobrazena.
Další dostupné překlady jsou dostupné zde.
Posted in Rubylation | no comments |