Stoprocentní mantra

Programátoři a jejich manažeři občas řeší, jaká míra pokrytí unit testy je dostatečná. Můžeme zaslechnout, že cokoliv pod 60% nestojí za zmínku, občas zahlédneme, že to magické číslo je 95% a někteří fundamentalisté dokonce tvrdí, že cesta k nirváně vede jen a pouze přes stoprocentní pokrytí.

Dnes jsem si řekl, že je ideální počasí na to, vyzkoušet jestli se dá sta procent dosáhnout. Povedlo se mi to během dopoledne a nyní bych se chtěl podělit o své zkušenosti.

Pracuji na jednom menším projektu, který v podstatě jen zpracovává REST volání a přeposílá je pomocí SOAP na další systémy. Není to nic složitého, ale není to ani žádná trivialita. Prostě ideální kandidát na pokusy. I před tím, než jsem se rozhodl dosáhnout vytoužené mety, jsem se snažil unit testy dělat poctivě. Jen občas jsem něco neotestoval s tím, že to je příliš triviální na to, abych se patlal s testem. Většinou jsem ale svoji lenost ovládl. Proto mě překvapilo, že moje pokrytí testy bylo něco kolem 60%.

Začal jsem pátrat po příčinách a zjistil jsem, že nejsem tak svědomitý, jak jsem si myslel. Narazil jsem například na neotestovanout třídu, kterou jsem jen odněkud zkopíroval Fůůůůůj. Ale občas je to potřeba. Znáte to, potřebujete něco podobného co umí nějaká metoda ve Springu, jenom je to potřeba trochu ohnout, má to implementovat jiné rozhraní a navíc je to v tom Springu private. Tak to vezmete, zkopírujete a trochu přiohnete. Pokud se pokoušíte o stoprocentní pokrytí, musíte to i otestovat. Pokud se spokojíte s pokrytím nižším, je velice lákavé si říci, že to už je vlastně otestovaný kód, tudíž není potřeba testovat. Ještě jednou fůůůj. Ještě že se stydím jenom před děvčaty. Nakonec jsem byl rád, že jsem tu zkopírovanou metodu musel otestovat. Díky tomu jsem se nad ní pořádně zamyslel a zkrátil ji na polovinu.

Pak jsem ještě napsal pár testů na metody, které jsem nepovažoval za hodné testu. Potěšilo mě, že jsem narazil jenom na jednu metodu, kterou jsem evidentně netestoval, protože by to bylo moc práce. Díky své nové stoprocentní mantře jsem se donutil otestovat i ji. Nakonec to ani moc nebolelo, jenom jsem se musel ponořit do vnitřností Spring-WS abych zjistil jak nejlépe vytvořit instanci jednoho rozhraní (SoapMessage). Alespoň jsem se něco naučil. Pak už zbývaly jen zapeklitější věci.

Get a Set metody
Jedním ze zádrhelů byly get metody. Poté co jsem otestoval všechen kód, který něco dělá, zbyla mi jedna skupina metod, které prostě v žádném testu nebyly potřeba. Byly to get metody u Spring bean. Když totiž potřebuji pomocí Springu něco injektnout, automaticky vygeneruji set a get metody. A to i když používám autowiring přímo do polí. Set metody využívám právě v unit testech. Ukázalo se, že pokud není v testu set metoda použita, je v tom testu něco špatně. A to bez výjimky. Buď to, co nastavuji, není vůbec potřeba nebo netestuji funkcionalitu, která danou hodnotu využívá. Takže si prosím zapamatujte:

Pokud není otestována set metoda, je něco špatně.

Ale zpátky ke get metodám u Spring bean. Ty pro test potřeba nejsou. Ony totiž nejsou potřeba vůbec. Jenom jsem měl hrozný psychický problém s tím je smazat. Pořád jsem si říkal, že je tam nechám, co kdyby byly potřeba. Co kdyby někdo potřeboval vědět, jak je daná třída nakonfigurována atp. Ale pak mě stoprocentní mantra donutila je smazat a myslím, že si na ně nikdo nevzpomene. Jenom je to divné mít set bez getu. Kdybych ty get metody opravdu někdy potřeboval, třeba kvůli JMX, tak je prostě přidám a otestuji.

Equals
Dalším špekem je equals metoda. Pokud byste náhodou nevěděli jak vypadá, tak vám ukáži, to co mi normálně generuje Eclipse

	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof Person))
			return false;
		Person other = (Person) obj;
		if (firstname == null) {
			if (other.firstname != null)
				return false;
		} else if (!firstname.equals(other.firstname))
			return false;
		if (secondname == null) {
			if (other.secondname != null)
				return false;
		} else if (!secondname.equals(other.secondname))
			return false;
		return true;
	}

A teď si představte jak otestovat všechny možnosti takového krasavce. Abyste dosáhli plného pokrytí, museli byste napsat cca devět testů. To vše kvůli vygenerovanému kódu. K tomu mě nikdo nedonutí. Zkusil jsem použít EqualsBuilder, ale moc jsem si nepomohl. I s jeho pomocí jsou tam asi čtyři kombinace, které je potřeba otestovat. Mumlaje si pod vousy „sto procent, sto procent“ jsem se zeptal pana Googla a ten mě navedl na EqualsTester. Problém vyřešen, jsem zas o krok blíže k vytouženému cíli.

Nemožná výjimka
Zbývala mi jen jedna úplně poslední past nastražená na mě pány ze Sunu:

	private byte[] toByteArray(String data)
	{
		try {
			return data.getBytes("UTF-8");
		} catch (UnsupportedEncodingException e) {
			LOG.error("Can not happen",e);
			throw new RuntimeException(e);
		}
	}

Abych dosáhl kompletního pokrytí, musím otestovat i catch blok. Ten je bohužel dosažitelný jen pokud vaše JVM nepodporuje UTF-8. Vzhledem k tomu, že by pak JVM nesplňovalo specifikaci není tato situace moc pravděpodobná. Bohužel, tím pádem ani otestování není moc snadné. Nakonec jsem musel kód změnit takto:

	private static final Charset UTF8 = (Charset)Charset.availableCharsets().get("UTF-8");

	private byte[] toByteArray(String data)
	{
		return data.getBytes(UTF8);
	}

Není to moc elegantní, ale alespoň se mohu utěšovat tím, že je to výkonově efektivnější. Pokud znáte lepší cestu, podělte se o ni, rád se něco přiučím.

A to je vše, dosáhl jsem stoprocentního pokrytí. Zde mi samozřejmě hrálo do karet, že jsem nemusel řešit připojení k databázi. To bych se musel uchýlit k mockování JDBC template nebo k mé oblíbené in-memory databázi ve spojení s JPA. To už by sice nebyl unit test, ale třeba by to nikdo nepoznal.

Takže mám stoprocentní pokrytí, co mi to dalo? Donutilo mě to otestovat věci, na které bych se pravděpodobně jinak vykašlal. U některých z nich jsem byl rád, že jsem je nakonec otestoval. Přivedlo mě to k lepšímu kódu. Potěšilo mě, že jsem nemusel psát žádné nesmyslně testy, jenom kvůli pokrytí. Toho jsem se ze začátku trochu bál. Samozřejmě nesmím usnout na vavřínech. To že mám kód zcela pokryt neznamená, že v něm není chyba. Těch tam naprosto jistě ještě hodně zůstalo. Nicméně mám z toho docela dobrý pocit, myslím si, že 100% je docela dobrá laťka. Na jednu stranu vám nedovolí vymýšlet si výmluvy, proč něco netestujete, na stranu druhou se ukázala jako dosažitelná. Proto se příště jako správný úderník pokusím pokrýt na 105%.

Není důležitá cesta, ale cíl

Není důležitý cíl, ale cesta k němu
Budha

Jsem členem týmu, který se zrovna formuje. To s sebou nese trochu tápání, diskuzí ohledně toho co a jak chceme dokumentovat, jak se mezi sebou domlouvat, jak testovat, jak dělat revize kódu a podobně. V poslední době se mi stává, že uprostřed živé diskuze vyslovím svoji zabijáckou otázku. Vždy je to něco ve smyslu „Proč to chceme dělat?“, „Jaký je cíl?“, „Pro koho to chceme psát?“ nebo „What’s the objective?“. Většinou si za podobnou otázku vysloužím pár pohledů, některé z nich říkají „Vždyť je to přece jasné“, jiné „Ty seš zas úplně mimo“. Málokdy zahlédnu pohled říkající „Děkuji, že jsi nás přivedl na pravou a jedinou cestu, ó velký.“.

Tak jsem si řekl, že napíši tento zápisek, aby kolegové věděli, jak ke mně mají vzhlížet. Myslím si totiž, že ať už člověk dělá v práci cokoliv, měl by se nejdřív zamyslet nad cílem. Napadají mě tyto příklady

  • Připravuji prezentaci. Musím se zamyslet nad tím co je ta nejdůležitější věc, kterou chci sdělit. Pak už bývá snadné vymyslet, jak má prezentace vypadat.
  • Píši dokument. Musím se zamyslet pro koho ho píši a při jaké příležitosti ho bude číst. Jinak vypadá dokument pro manažery, který ho (možná) bude číst jednou při schvalování a jinak pro programátora, který v něm bude občas něco hledat.
  • Organizuji schůzku. Musím se zamyslet co je ten nejdůležitější výstup. Má někdo něco zařídit? Chci se něco dozvědět? Jakmile v tom mám jasno, mám z půlky vyhráno.
  • Dělám revizi kódu. Musím se ujistit, že vím proč ji chci dělat. Chci se ujistit, že nováčci píší kód který je konzistentní se stávajícím? Vím že revizi musí dělat zkušenější kolega. Chci zachytit největší chyby co nejdřív? Musím dělat revize průběžně. Chci šířit znalosti? Stačí mi jedna revize na konci za účasti všech.
  • Píši aplikaci. Měl bych vědět, co je ten hlavní důvod její existence.
  • Jdu se pohádat s kolegou. Nejdřív bych se měl nadechnout a zamyslet se nad ideálním výstupem z hádky. Čeho jí chci dosáhnout?
  • Píšu zápisek do blogu. Co je ta hlavní myšlenka, kterou chci sdělit?

Zní vám to povědomě nebo dokonce samozřejmě? Není divu. Se stejnou myšlenkou se setkáváme na různých školeních, v agilních metodikách, v chytrých knihách, zkrátka všude. Zamyslete se ale, jak často tento postup dodržujete. Mě se to povede jen občas a to se docela snažím. Ono to totiž není vůbec jednoduché.

Na každou otázku existuje jednoduchá, snadno pochopitelná, nesprávná odpověď.

Ještě víc se to zesložití, když vám řeknu, že ten cíl by měl být ideálně jen jeden. Já vím, že chcete zabít víc much jednou ranou. Je ale neuvěřitelně užitečné zformulovat jediný snadno popsatelný cíl. Pak si můžete nadefinovat i pár vedlejších cílů, ale pořád musíte vědět, který je ten primární. Je to podobné jako měli posádky bombardérů za druhé světové. Ty také dostali jeden primární cíl. Pak samozřejmě měli i pár druhotných cílů, kterým se měli věnovat, kdyby jim náhodou nějaká bomba zbyla nebo kdyby byl prvotní cíl nedosažitelný. Ale ten hlavní byl jen jeden. Proč to tak bylo? Přece proto aby věděli kam letět. Kdyby dostali cílů dvacet, tak by jim to k ničemu nebylo.

My jsme v úplně stejné situaci. Jakmile si řekneme, že dokument je určen pro manažery, ale vlastně i pro programátory a přihodíme tam i pár kapitolek pro testery, tak nakonec skončíme s dokumentem, který nebude vhodný pro nikoho.

Dejme tomu, že jsme investovali velké úsilí a dost času a máme cíl, s který je jasně sdělitelný a pochopitelný. Čeká nás další krok. Musíme cíl roztroubit. Musíme zajistit, aby všichni zúčastnění věděli co ten cíl je. Napíšeme to do pozvánky, na začátek dokumentu, řekneme to v úvodu, prostě se ujistíme, že to všichni vědí. U opakovaných akcí to čas od času zopakujeme. Ještě lepší je, když se na cíli zúčastnění dohodnou sami, ale to už bych toho chtěl moc.

Promiňte mi tento dlouhý dopis, ale neměl jsem čas napsat krátký.
Pascal

Jakmile mám super cíl a všichni o něm vědí, můžeme si začít užívat jeho pozitivních účinků. Nejlépe je to vidět na schůzkách. Když lidé vědí proč tam sedí, vědí o čem mají mluvit. Organizátor dokonce může schůzku nasměrovat, když se stáčí někam kam nemá. Lidé dokonce dopředu vědí, jestli jsou na té schůzce potřeba. Někdy se dokonce při vymýšlení cíle schůzky zjistí, že není vůbec potřeba, že se ten cíl dá naplnit úplně jinak, jednodušeji.

To je hrozně důležité. Při vymýšlení cíle, často přijdeme na to, že k jeho naplnění by se nám hodily úplně jiné prostředky.

Cíle také umožňují prioritizaci. Pokud vím co je cílem iterace, mohu si při nedostatku času vybírat, které úkoly jsou důležitější. Všechno co s cílem nesouvisí musí pryč. Stejně tak u dokumentů, co neslouží k cíli tam nemá co dělat. O prezentacích nemluvě, dobrá prezentace se pozná podle toho, že je jasné co se vám přednášející snaží říci.

Zbývá nám tedy otázka, proč se to tak málo vidí? Proč chodíme na schůzky, u kterých nevíme proč jsou svolávány, proč čteme dokumenty, u kterých není jasné komu jsou určeny, proč píšeme programy, u kterých si nejsme jisti k čemu mají sloužit? Důvodů je asi víc.

Hlavně si všichni myslí, že cíl je přeci jasný. Všichni přeci víme, proč se každý týden scházíme a říkáme si co kdo dělal a bude dělat, všichni vědí, proč píšeme ten který typ testů, všichni přece víme proč děláme revizi kódu, všichni víme proč píšeme design. Opravdu? Tak mi to řekněte jednou větou.

Mám podezření, že dalším problémem s cílem je, že nutí lidi myslet. Já například myslím hrozně nerad. Je jednodušší svolat schůzku a o něčem si povídat, než si lámat hlavu s cílem. Jasné cíle také lidi neuvěřitelně omezují. Když třeba dělám prezentaci, tak chci předvést co všechno o tématu vím, nechci se nějak omezovat. Když píšu dokument pro manažery, chci se vytáhnout, tak tam švihnu jak bude vypadat databáze. Když píšu blog, tak chci předvést jak jsem skvělý, ne se soustředit na na nějaký trapný cíl. Na druhou stranu, kdybych to udělal, byl by i tento zápisek dvakrát kratší a mnohem lepší.

Vedeme lidské bytosti

Mítinky jsou boje o moc mezi lidmi, kteří něco chtějí, a těmi, kteří jim to nechtějí dát.

Pravidelnější čtenáři tohoto nepravidelného blogu už jistě tuší, že je čeká další minirecenze. Tentokrát se jedná o knihu „Managing humans“ (na ten odkaz rozhodně klikněte) od Michaela Loopa (Jírovy patří dík za tip). Kniha je to stylem podobná Joel on Software a musím přiznat, že se mi líbila skoro stejně. Je sice o hodně méně technická, ale zato mnohem víc o lidech.

Dal bych ji za povinnou literaturu lidem, kteří se snaží vést (nebo nedejbože řídit) bandu nerdů. Ale je hodně zajímavá i pro nás nerdy samotné. Hodně užitečná je i pro lidi, kteří pracují v korporaci.

Takže co se v knize dočtete? Hodně mě zaujala kapitola o tom, jak uhodnout, co se děje na pracovní schůzce. Znáte to, sedíte na mítinku a přemýšlíte co tam sakra děláte, co a proč se ti lidé snaží říci. V kapitolách „Agenda detection“ a „Meeting creatures“ se to dočtete.

Zajímavé jsou i části, které popisují jak se připravit na pohovor, napsat životopis a podobně. To jsem měl sakra vědět před pár měsíci.

No a neméně zajímavé jsou i kapitoly o nerdech samotných. Například NADD. Ale třeba “Přiručka k nerdovi” se do knihy nedostala.

Jak jste si všimli, většina kapitol je volně ke čtení i na autorově blogu, ale mě se kniha čte mnohem lépe. Jenom si dovoluji upozornit, že je to psané pokročilejší angličtinou, takže se docela procvičíte. Například na straně 188 mě dostalo sloveso „to grok“, ale jelikož jsem nerd tak jsem to pochopil i bez slovníku. I když jak tak koukám, tak se to dostalo i do slovníků, zajímavé. Ale zpátky ke knize, ta zaslouženě dostává neuvěřitelných deset hvězdiček z deseti.

No a abych vás neochudil o tradiční ukázku z knihy, uvedu pár položek z vysvětlivek na konci knihy:

Agenda – Seznam věcí, které se musí na dané schůzce odehrát aby byla kompletní. Pokud všichni účastníci na dané schůzce nejsou seznámeni s agendou, bude docházet ke ztrátě času.

Spolupráce – Slovo používané k tomu, aby vás přesvědčili pracovat s lidmi, kterým byste se raději vyhnuli.

Milník (Milestone) – Chabě nadefinované, přehnaně vytrubované datum, které je částí softwarového vývoje. Vývojový tým při něm přemýšlí o tom jak je v pytli.

Proces – Šestipísmenné slovo začínající na P. Proces není špatná věc. Obzvlášť ve velkých organizacích kde ohromné množství lidí ztrácí velké množství času děláním té samé věci.

Specka (Specifikace) – Dokument, který říká jak to je. Proces psaní specifikace bývá víc užitečný než výsledný dokument.