Unit testování entit – vaše názory
- Filip Klimeš
- Nette Blogger | 156
Poslouchám přednášku o Mockery od @hrach a zarazil jsem se nad větou „entity jsou jenom trochu hezčí přepravky na data .., je to stejný jak pole“.
Musím silně nesouhlasit, protože pokud se nedodržuje anemic domain model anti-pattern, entity nesou i poměrně klíčovou bussiness logiku, kterou je setsakramentsky vhodné testovat, resp. mockovat. Co si myslíte Vy (zejména @Tharos)?
Editoval Filip Klimeš (20. 3. 2015 16:14)
- Jan Tvrdík
- Nette guru | 2595
Hrach to řekl akorát realisticky, naprosto drtivá (IMHO pochopitelně) většina aplikací nemá doménový model a anemic domain model jim nezpůsobuje výrazné problémy, spíš naopak přináší výhody ve snížené mentální komplexitě aplikace. Pokud máš dobře napsaný doménový model, tak se na tebe Hrachův výrok nevztahuje.
- hrach
- Člen | 1838
Velmi mě mrzí, že ten slajd zazněj jak zazněl, myslím, že by stál aspoň za 2 přednášky. Rád bych to teď doplnil:
- úvodní otázka lehce naznačuje, že entity není třeba testovat, nadpisem a pomocí „entity nesou i poměrně klíčovou bussiness logiku, kterou je setsakramentsky vhodné testovat“. To jsem neřekl a rozhodně souhlasím, že je třeba testovat entity, jakmile nesou (byť menší) logiku.
- osobně jsem raděj nakolněn blíže anemic domain model než na opačnou stranu, nicméně sám ho nedodržuji, osobně razím ve všech případech pro mě pragmatický přístup.
- mockovat přepravky na data – to nemám rád. To, že ty přepravky mohou
být chytřejší jim ještě neodebírá status přepravek. Z mého pohledu je
podstatné, co daná třída s přepravkou dělá a co přesně ta přepravka
v té „vyšší logice“ dělá. Př: z mých různých projektů:
class Poll extends Entity implements IHasAudioFile { public function getAllowedResponses() { $keys = []; foreach ($this->answers as $answer) { $keys[] = $answer->keyCode; } return $keys; } } class Blog extends Entity { /** * @param int $sockeId * @return BlogGadget[] */ public function getGadgetsForSocket($sockeId) { $gadgets = []; foreach ($this->gadgets as $gadget) { if ($gadget->socketId === (int) $sockeId) { $gadgets[] = $gadget; } } return $gadgets; } }
V tuto chvíli se nejedná již o čistý anemický model; Je třeba toto otestovat zvlášť, nicméně myslím si, že testovat to znovu v jiných částech aplikace tím, že to budu mockovat, je hloupost a daň za to je vysoká – obrovské množství mockovacích objektů – viz. předchozí slajd.
- Do entity lze napsat mnohem více logiky. Ještě jsem (bohužel) neviděl entitu, která by v sobě měla více vyšší logiky než výše a přitom by dodržovala všechny jiné standardy psaní kodu. Kdyby ses podělil o příklad své entity (klidně celé, na gistu), rád bych se podíval a zaujmul stanovisko.
- tomuto tématu jsem se nevěnoval víc, protože se myslím, že je velmi velmi úzce spjato s konkrétní knihovnou a tvojí implementací a nelze zde obecně doporučovat nějaké jednoznačné popstupy.
- poznámka Honzy Tvrdíka je přesná.
Editoval hrach (20. 3. 2015 18:52)
- Filip Klimeš
- Nette Blogger | 156
Omlouvám se, můj text vyznívá útočně. Měl vyznít pouze nesouhlasně a otevřít diskusi.
@JanTvrdík: To si napsal skvěle, takhle mi to dává celé smysl.
@hrach:
Já osobně jsem dospěl k Rich Domain Modelu tou těžší cestou.
Až donedávna jsem se silně přibližoval anémickému modelu, až mi ve
škole otevřeli oči a já zjistil, jak moc velký anti-pattern to je
a kolik práce mi ve skutečnosti přidělával.
Až zpětně jsem pochopil Tvůj pragmatický přístup. Nicméně bych rád podotkl, že porušovat pravidla je v pořádku, když víš, proč je porušuješ. Proto si myslím, že by v přednášce stálo RDM vs ADM za zmínku, aby si to náhodou někdo nevyložil špatně. Mluvím z vlastní zkušenosti, kdy jsem si špatně vyložil pověstný pětivrstvý model Honzy Tichého.
Obecně si myslím, že dobrý příklad jsou entity v Tharosově příkladu. Minimálně pro ověřování, jestli byly na entity volány příslušné metody.
Já osobně jsem narazil na jeden případ, kdy se mockování entity vyloženě nabízelo. Mám entity s dědičností, na které jsem aplikoval Visitor pattern. Všechny servisy, které s entitou pracují, implementují EntityVisitor interface a pomocí double dispatch jim entita řekne, jak se k ní mají chovat. Velmi stručný a naivní GIST je tady. Pokud bych chtěl např. testovat visit*() metody, mohl bych namockovat Entity a zjistit, jestli testovaná třída dále volá na entitě to co má.
Editoval Filip Klimeš (20. 3. 2015 20:41)
- Filip Klimeš
- Nette Blogger | 156
@hrach: Ještě jednou jsem si to poslechl a přečetl si Tvůj post a zjistil jsem, že jsem špatně pochopil vyznění celé věci – a to tak, že nemá smysl testovat entity, přičemž si mluvil jen o mockování. Omlouvám se.
Editoval Filip Klimeš (20. 3. 2015 21:20)
- Tharos
- Člen | 1030
@FilipKlimeš: Pravděpdobně jsi přehlédl testy v té mé RPG ukázce, které jsem vzorně napsal. ;) Je tam ukázka testů právě na logiku v entitách.
Entity, které zapouzdřují nějakou logiku, je rozhodně žádoucí testovat! V tomhle se nijak neliší od jiných tříd, které se běžně testují. Skoro bych řekl naopak – logika, kterou entity nesou, bývá často velmi klíčová a je v první linii „záležitostí“, které se vyplatí mít otestované.
Důkladně napsané Doctrine entity se testují obecně velmi dobře. Stačí
si hlídat, abys v konstruktoru inicalizoval kolecke (typicky pomocí
ArrayCollection
) a pár dalších drobností (inspiraci hledej třeba zde), a ve výsledku máš třídy, jejichž instance
můžeš vytvořit (a uvést do požadovaného stavu) kompletně bez použití
databáze a pak na nich cokoliv testovat. Jde to jako po másle.
V případě jednotkových testů je pak samozřejmě ideálem, aby se
v rámci daného testu použilo klíčového slova new
pouze
u třídy, kterou testuješ, a vše ostatní bylo namockované. To třeba
v té výše odkázané ukázce dodržuji ;). No a v rámci akceptačních
testů samozřejmě testuj cokoliv podle libosti. :)
- Filip Klimeš
- Nette Blogger | 156
@Tharos: vida, přehlédl :) a přesně takhle jsem si je představoval. Se zbytkem souhlasím bez výhrad.
@hrach: Tak to jsem rád. :)