Unit testování entit – vaše názory

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Filip Klimeš
Nette Blogger | 156
+
+6
-

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
+
+4
-

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 | 1834
+
+1
-

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
+
0
-

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
+
0
-

@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
+
+3
-

@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. :)

hrach
Člen | 1834
+
+1
-

@FilipKlimeš V pohodě! :) Já to vůbec nechápal útočně.

Filip Klimeš
Nette Blogger | 156
+
0
-

@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. :)