Včera jsem měl na první české PyCON konferenci přednášku o pytestu. Na Twitteru se rozjela diskuze o tom, že můj ukázkový test není unittest. Což je teda úplně mimo moji přednášku, protože jsem mluvil o unittestu jako toolu, nikoliv typu testu, vs. pytestu. A integrační test jsem vybral, protože je to prostě dobrá ukázka pro běžnou praxi.
Každopádně bych chtěl sdílet můj názor na toto téma. V Twitter vlákně se řeší několik věcí – udržovatelnost takových testů, zda mít různé typy separátně, spolehlivost, schopnost najít co nejvíce chyb a hlavně teorie. A jak já rád říkám, teoreticky mezi teorií a praxí není rozdíl, ale prakticky… :-)
Samozřejmě, nejlepší možný test je unittest, který nepotřebuje nic. Žádné cizí rozhraní, žádnou databázi, žádný přístup na disk ani nikam jinam, prostě nic. Pokud něco takového lze udělat a otestuje se tím vše potřebné, pak máte vyhráno. Pak vás nemusí zajímat boj s integračními testy. I to je důvod, proč Selenium je pro puristy tak děsivé.
Jenže v praxi to tak nefunguje. V praxi často něco čteme a zapisujeme. Můžeme takovou aplikaci otestovat tak, že vrstvu zařizující komunikaci s databází či něčím jiným nějak nahradíme/vyřadíme. Jenže, co jsme tím získali? Nic moc ve skutečnosti. Máme otestované algoritmy a provázanost naší aplikace, ale vůbec nevíme, zda funguje. Mohu otestovat, že moje aplikace správně vygenerovala XML a nikam ho neodeslat. Jenže po nasazení mi mohou klidně poslat neskutečné množství uhlí…
OK, špatný příklad. Ale ten příběh mne fakt pobavil. :-) Vezměme jiný příklad: chci otestovat statistiky. Ano, mohu při testování podsunout aplikaci data, která chci, a podívat se, zda se čísla přelila takhle tam, tohle zase tam, a výsledek je takovýhle. Jenže… jenže po nasazení do provozu zjistím, že v SELECTu je překlep v jednom případě. Který sice testuji, ale bez databáze.
A takhle to je se spoustou aplikací. Proto, abychom měli větší jistotu, testujeme s databází. A pokud je to pouze s databází, říkáme tomu stále interně unittest. Pokud je to unittest bez databáze, říkáme tomu pure unittest. Pokud voláme i cizí rozhraní, zapisujeme na disk, …, říkáme tomu integrační test.
Řešili jsme, jak takové testy rozdělit. Původně jsme je měli rozdělené na
„unittesty“ a na integrační testy. Ale byl v tom guláš, co kde vlastně
testujeme a proč. Někdy bylo prostě vhodnější nechat integrační test, někde
zase naopak. Takže testování nějaké funkcionality máme v jednom souboru a
rozdíl mezi testy řešíme označením pomocí dekorátoru @pytest.mark.*
. Tak
hned vidím všechny testy pohromadě.
A jak nám to funguje? Velice dobře. Testy máme rychlé, v průměru 125 ms na test (s tím, že to hodně zvedá především pár integračních testů kvůli účetnictví pod Windowsem). S databází nemáme problémy. Dokonce CI tool (u nás Jenkins) si pro každý běh nejprve vytvoří novou databázi (cca deset vteřin). Tedy máme pod kontrolou, co v databázi je. Najde nám to více chyb. Nespočet chyb bylo v SQLku… Nemluvě o tom, že nemusíme složitě mockovat.
Abych to shrnul: integračních testů se nezbavíme a jsou potřeba. S knihovnou
unittest
je těžké takové testy udržovat a měli jsme s tím opravdu problémy.
Ale díky pytestu a tipům, o kterých jsem mluvil a dříve i
psal, už problémy žádné
nemáme, ba dokonce je to stejně snadné jako jednotkové testy.
P.S.: Omlouvám se všem puristům za zemřelá koťátka. :-)