31. prosince 2009

Největší problémy při zavádění testování

Mám teď možnost zavádět testování do již existujícího projektu. Projekt je to celkem velký - přes 10 vývojářů, přes 100 tabulek, přes 50 tisíc tříd, několik let vývoje. Technologicky je to postavené nad Springem, Hibernate a spoustou dalších knihoven. O zavedení testování se již pár lidí přede mnou snažilo, ale vždy bez úspěchu, tak jsem zvědavý, jak teď dopadnu já. Hodně důležité je, že testování chce nyní i vedení, což je moc důležitý předpoklad - psaní a údržba testů stojí spoustu úsilí a času a pokud není podpora ze shora, tak se to musí pak "pytlíkovat" za zády a to není moc dobré.

Mám dojem, že většina vývojářů by testy psala celkem ráda, ale vždy diskuze na toto téma skončí u toho, že aplikace je to velice složitá, a že je problém vůbec nějak nastavit počáteční data pro testy.

Ještě je brzy na to, abych psal jak to celé dopadlo, to ještě bude nějaký čas trvat. Dnes bych chtěl psát o největších problémech, které jsem zatím musel řešit, abych vůbec mohl napsat nějaké testy:

  • "špagety kód" - určitě největší problém. Aplikace se vyvíjí již řadu let a zejména ten starý kód není nic moc. Když to přeženu, tak vše souvisí se vším a pak je problém inicializovat pro testy jen ty části aplikace, které potřebuji. Musel jsem refaktorovat, psát stub objekty, abych se dokázal vymezit. Tady není možné praštit do stolu a říci, že zítra se celá aplikace zrefaktoruje a bude to ok. Je to postupný proces. Tento problém se netýká jen samotného kódu, ale také konfigurace aplikace. Musel jsem jí rozdělit do částí tak, abych mohl např. inicializovat jen připojení k databázi nebo webové služby.

  • propojení s dalšími aplikacemi - aplikace závisí na spoustě dalších dílčích aplikací a proto je nezbytné se umět vymezit, nejlépe pomocí nové implementace konektorů pro testy (stub vs. mock objekty).

  • špatná dokumentace - to neplatí jen pro testy, ale obecně. Abych mohl co nejlépe a nejjednodušeji používat nebo implementovat rozhraní, tak potřebuji kvalitní dokumentaci (JavaDoc), jinak si pak musím vše dohledávat v kódu sám.

  • používání auto-wiringu - Springovský auto-wiring se používá pro konfiguraci celé aplikace, takže na první pohled nejsou vůbec jasné vazby mezi třídami, komponentami. Zde jsem musel hodně zapojit IDEU (zejména výborný nástroj na analýzu závislostí v kódu), abych se dopátral všech možných závislostí. Držel bych se doporučení autorů Springu a auto-wiring bych používal jen pro testy.

  • generované ID beanů - pokud v konfiguraci beanů neuvedeme atribut id nebo name, tak to neznamená, že daný bean identifikátor nemá, ale že se automaticky generuje, standardně podle jména třídy (rozhraní BeanNameGenerator). Takže když pak změním jméno třídy, tak se mi změní i identifikátor beanu. Je proto určitě lepší vždy definovat identifikátor, ideálně přes atribut id.

  • transakční propagace REQUIRES_NEW - testy nemají mít žádný vedlejší efekt, žádná uložená data v databázi po testech. Sem tam je v aplikaci použita propagace REQUIRES_NEW, což znamená, že v případě existující transakce se aktuální transakce pozastaví (suspenduje), spustí se nová transakce, která se pak kamitne a pak se znovu aktivuje původní transakce. Toto nemám ověřené, ale vidím zde možný problém pro testy, kdy by se vlastně měly všechny transakce na konci rollbacknout, ale co tyto vnořené transakce, které již jsou po komitu? REQUIRES_NEW se používá minimálně, tak možná to nebude potřeba ani testovat a když bude potřeba, tak zkusím následující možnosti - upravit transakčního managera nebo refaktorovat produkční kód.

Opět se mi potvrdila moje dřívější zkušenost, že čím kvalitnější produkční kód bude, tím lépe se mi budou psát testy. V tomto také vidím jeden z největších přínosů testování.

4 komentáře:

Vít Šesták 'v6ak' řekl(a)...

"psaní a údržba testů stojí spoustu úsilí a času"
Vytržené z kontextu je to rudý hadr na býka. Myslím, že ve výsledku testy čas šetří. Ano, v tomto případě je to velká jednorázová časová investice, ale nevím, jestli všichni si to takto zapojí správně do kontextu.

Petr Jůza řekl(a)...

V tomto případě je potřeba do toho hodně investovat, zejména čas (na samotné psaní, na školení, na úpravu stávajícího kódu apod.).

Myslím, že velký problém testování je ten, že ty přínosy se špatně měří - já věřím, že psaním testů jsem schopen odhalit chyby, zlepším tím kvalitu aplikace, ušetřím si čas řešením budoucích problémů, rychleji vyvíjím, když nemusím pořád dokola pouštět aplikační server, ale nemám nebo nevím, jak to převést do řeči čísel. To je věc, které zase rozumějí manažeři a ty o tom rozhodují.

Ještě jsem nenarazil na žádnou studii, která by toto nějak potvrzovala.

Honza Novotný řekl(a)...

Dopisovat testy zpětně skutečně stojí spoustu úsilí a času a je opravdu velké riziko, že dřív než se začnou objevovat pozitiva, tým nebo vedení nevydrží a celá aktivita se stopne. Takže, Petře, držím palce.

My u starých netestovaných knihoven píšeme testy pouze pro novou funkcionalitu a před změnami regresní testy - to relativně jde, ale i tak je to plno práce, protože kód vůbec není psán tak aby byl dobře testovatelný.

U nových knihoven bych řekl, že psaní testů pro zběhlého programátora čas nejenže nenavyšuje, ale v řadě případech spíš šetří. U nově psaného kódu je to prostě bez diskuse.

Petr Jůza řekl(a)...

Testy zpětně budeme dopisovat jen u stěžejního kódu, u věcí, kde jsou dlouhodobě největší problémy a také tam, kde se nově budou objevovat chyby.

Jak ale jinak začít, když nikdo pořádně nemá povědomí o tom, jak psát kód, který by šel co nejlépe testovat? Začít se prostě musí. Není vyjímkou, že se píšou celkem oddělené části (konektory, moduly), takže zde by to mohlo jít snad dobře.