Co mě testy naučily o mém kódu
Návrat ke kořenům anebo někam kde jsem možná nebyl. Možná to bude znít jako klišé, a do jisté míry to i klišé i je, ale ukaž mi tvoje testy řeknu jaký jsi programátor. Jestli jsem někdy mluvil o tom, že většina testů je ze své podstaty povahy integračními a to pro většinu enterprise aplikací, které jsem psal, pak jsem se šeredně spletl. Dneska bych své tvrzení poupravil na, ano i integrační testy jsou potřeba, ale jejich počet by měl být alespoň u javového kódu výrazně v neprospěch tech jednotkových (unitových). Celý problém integračních testů, které vytváříme na úkor testů jednotkových je, že se nám zřejmě píší jednodušejí neboť náš kód je procedurální.
Když jsem pracoval na HP SOA Sysyinet, měli jsme celé baterie integračních testů. Tam bych odhadoval poměr 95% ve prospěch integračních testů. Integrační test je pro mě pochopitelně i ten typ testu, který potřebuje ke své činnosti nastartování nějakého kontejneru např. aplikační kontext Springu. Díky bohu za ty testy, ale nejenom tyhle, ale i jiné integrační testy mají jinou vypovídající schopnost o kódu, který píšeme. Musel jsem napsat hodně kódu, abych v hlavě udělal tenhle mentální kotrmelec.
Proč preferovat jednotkové testy
Jednotkové testy nás nutí programovat objektově. Naopak integrační testy nás nenutí vůbec k ničemu. Vezmeme prostě tu nejvyšší vrstvu špaget a tu otestuju. To má samozřejmě za následek, že ten kód ve spod je hůře otestovaný, protože píšeme proti vrstvě horní. Ve chvíli kdy píšeme jednotkový test, tak nás to nutí mnohem více k tomu, abychom ty špagety rozpletly do objektů s vlastní odpovědností a ty potom jednoduše mockovali.
Vedlejším produktem jednotkových testů je i to, že mnohem více přemýšlíte a jste nuceni přemýšlet o rozhraních mezi vašimi objekty. To jsou věci, které se vám při špagetovém programovaní poměrně zatemňují.
Řekněme, že máme třidu, která bude stahovat RSS soubor z dané URL a bude vracet titulky příspěvků. Zjednodušený kód by možná vypadal nějak takhle.
public class RssDownloader {
public String[] getTitles(Url rssUrl) {
HttpClient httpClient = new HttpClient();
Xml xml = new Xml(httpClient.get(rssUrl));
String titles[] = new String[];
Node items[] = xml.getElements("Items");
for(Node item : items) {
titles.add(item.getTitle());
}
httpClient.close();
return titles;
}
}Problém tohle třídy je v tom, že míchá dohromady dvě věci. Stažení RSS souboru a jeho zpracování. Správně bychom měli zpracování vlastního kódu držet pryč od toho odkud to XML pochází. Co když to RSS budeme najednou číst třeba z filesystému a nebo z databáze. Špatný objektový návrh, ale to si možná neuvědomíme dokud nebudeme ten kód chtít otestovat. Protože budete muset napsat integrační test a někde si nastartovat HTTP server. Ve skutečnosti, ale nechcete testovat HTTP klienta, ale otestovat, že z daného XML kód načte to co si představujeme.
Správné řešení je proto zobrazeno na dalším obrázku. Původní obrázek
public class RssDownloader { private final RssTitleExtractor titleExtractor;public RssDownloader() { this.titleExtractor = new RssTitleExtractor(); }
public RssDownloader(RssTitleExtractor ext) { this.titleExtractor = ext; }
public String[] getTitles(Url rssUrl) { HttpClient httpClient = new HttpClient(); Xml xml = new Xml(httpClient.get(rssUrl)); String titles[] = titleExtractor.getTitles(xml); httpClient.close(); return titles; } }
public static class RssTitleExtractor {
public String[] getTitles(Xml xml) {
String titles[] = new String[];
Node items[] = xml.getElements(“Items”);
for(Node item : items) {
titles.add(item.getTitle());
}
return titles;
}
}Pak je možné ono zpracování otestovat bez nutnosti mít integrační test. Můžete si dokonce, a měli byste si mockovat tu XML abstrakci. Samozřejmě ještě by šel HTTP klient přesunout jako instanční proměná třídy, zamockovat jej a pak testovat, že jej RssDownloader dobře používá (viz volání close metody).
Na tomhle jednoduchém příkladu jsem ukázal, že objektové programování, v tomhle případě demonstrované na správném oddělení odpovědností objektů, vede k tomu, že kód můžeme pohodlně testovat jednotkovými testy.
Verze na Medium · Archiv původní stránky
Související články
Související epizody CZ Podcastu
- #3 · Java 6 SE, Vývoj webových aplikací
Dagi a Filemon o novinkách v Javě 6 a webových frameworcích.
- #1 · Vývojová prostředí v Javě
První díl CZ Podcastu se věnuje vývojovým prostředím pro Javu.