Obecně k testování – kontorlovat vstup nejníže + databáze
- Jiří Nápravník
- Člen | 710
Pokouším se testovat a narážím na takové obecnější věci a byl bych rád za uvedení.
- Řekněme, že mám fasádu, která vytáhne věci z databáze, které jsou seřazené a předá je dál service, která je bezstavová a dá se tak dobře otestovat. Nemusím sahat na databázi. Otázka zní, měl bych testovat v té service, zda jsem dostal seřazená data? Podle mě jo, protože navenek je to public, může to kdokoli zavolat, tak bych to měl otestovat, zda jsou seřazené, případně bych to měl doseřadit. Na stranu druhou pokud půjde člověk přes fasádu, tak je tam to řazení zbytečně navíc…
- Řekněme, že chci vytáhnout z databáze deset nejčtenějších článků. Je tohle vůbec řešitelné nějak rozumněji než, abych použil databázi? Přece jen sestavení tabulek, dotazování databáze něco zabere a není to úplně časově nejlepší.
- Pavel Kravčík
- Člen | 1195
Výstup si můžeš uložit třeba do texťáku a ten používat dokola v testech. V podstatě si napíšeš nějakou obdobu svojí cache.
- petr.pavel
- Člen | 535
Technicky je možné testovat i db – v setUp ji vyrobíš a naplníš,
v tearDown roztrháš.
Tím otestuješ i SQL dotazy, ale je to pomalejší a je víc práce
s aktualizací testovacích dat.
Rychlost můžeš vylepšit tím, že budeš používat třeba in-memory SQLite, ale pak v ní nefungují MySQL (nebo co používáš)-specific věci.
Aktualizaci dat si můžeš usnadnit tím, že je nebudeš uchovávat jako SQL dumpy, ale PHP pole (nebo Neon/Yaml/CSV) a do db budeš importovat z nich. Nebo mít master db, ze které děláš kopii. Ale když je dat víc a ještě prolinkované přes cizí klíče, tak je to prostě oser.
Takže odpověď není jednoznačně ano / ne, ale seznam pro a proti, a business se rozhodne, jestli mu to za ty náklady stojí.
Já to dělám tak, že co nejvíc logiky mám bez dotazů do db (jako máš ty v té service), a ty pak můžu unit testem proklepat ze všech stran na nejrůznějších datech, které mám v @dataProvider. Někdy mám získávání dat aspoň v metodách, které namockuju.
Krom toho mám hrstku integračních testů, které to projedou celé skrz
naskrz, včetně databáze.
A pak mám smoke testy – seznam url, které nesmí házet chybu. Ty jsou
taky s db, ale nekontrolují všechny stavy.
Dělám na malých projektech, kde jsou zákazníci v pohodě, když tam
občas vznikne chyba.
Nějak se mi ani nestává, že bych měl chybu v dotazu do db. Složitější
logiku nepíšu v SQL, ale v PHP. Jestli je i to řazení kritické, tak ho
dej taky do PHP.
Editoval petr.pavel (7. 4. 2022 10:30)
- Jiří Nápravník
- Člen | 710
Díky moc za reakce pánové. Vnesli jste mi do toho trochu světla.
@petrpavel U toho řazení, tam mám na mysli. V jedné metodě vytáhnu data seřazené už z databáze. A výstup pošlu do nějaké service, který jej má zpracovat. Když vezmu takhle aplikaci jako celek, tak je to v pohodě, ale pokud pracuju s více lidma, může někdo poslat do té service neseřazené věci, a teď je otázka, zda tohle taky testovat, zda jsou ta data seřazená, když přijdou do service…
- Polki
- Člen | 553
@JiříNápravník A proč si ta service netahá ta data z té
databáze pomocí té fasády?
Z jakého důvodu fasáda co tahá data z databáze je pak posílá do služby
a ne naopak, tedy proč si služba netahá ta data z té fasády?
Respektive pokud bych měl například nějaký datagrid, tak ty říkáš, že například Presenter vezme fasádu, zavolá nad ní findData a to, co vrátí ta fasáda, tak Presenter vezme, vytvoří službu datagridu a té předá ta seřazená data? Pak ano, musíš v té službě testovat, jestli data přišla v pořádku a jestli je požaduješ seřazená, tak ano, musíš testovat i to seřazení.
Proč to ale není jinak a to tak, že datagrid bude jako závislost brát například tento interface:
interface DataProvider
{
public function findOrderedData(int $page, int $itemsPerPage, string $sortColumn, bool $sortDirection): array;
}
A z něj si ta data sám pomocí této metody tahat? Ze signatury metody vyplývá, že ten, kdo ten interface implementuje, tak musí tuto metodu implementovat taky a ta již MUSÍ ta data vracet seřazená. Tedy to, jeslti ten provider seřazená data vrátil, nebo ne už není starost té servicy, jelikož ta počítá s tím, že kdo to implementoval, tak dodržel pointu té metody a toho interface, což se mimo jiné testuje pomocí UNIT testů.
Respektive pomocí mnou navrhovaného řešení by fasáda implementovala
initerface DataProvider a ta ‚service‘ která ta seřazená data potřebuje
by brala jen instanci DataProvideru, tedy té tvé fasády.
Díky tomu získáš hned 3 výhody.
- Nemusíš v service řešit, jestli instance implementující rozhraní DataProvider vrátila seřazená data, jelikož to je popisem toho interface, což je již otestováno UNIT testama.
- Tím, že netaháš data z databáze a neposíláš tam výsledná data, ale jen objekt, který se stará o získání těchto dat v tvou požadované formě docílíš toho, že daná service si ta data vytáhne až když je potřebuje, takže aplikace je pak víc lazy.
- Provider můžeš do servicy předávat přes DI, protože provider může být taky služba. Tím ušetříš tuny kódu a jak se říká, LESS CODE, MORE SECURITY. A taky se míň napíšeš :D Což je výhoda, jelikož práci dokončíš za kratší čas a dostaneš víc peněz. :D
EDIT 1
Ještě jsem zapomněla napsat odpověď na jednu tvou otázku. Ani v mnou
poskytnutém řešení není třeba sestavovat databázi a tu si testovat, ale
je možné, jelikož datagrid požaduje DataProvider, si DataProvider mocknout a
tedy UNIT testy dané servicy mohou proběhnout v pohodlí domova bez
přístupu k databázovému serveru.
EDIT 2
Ještě jedna věc… :D Alternativně nemusí být nastavení v direktivě té
metody v DataProvideru, ale může ten DataProvider sloužit jako nějaký
builder dat, kde bude už jen metoda build/get apod., díky čemuž nebude muset
ta tvá služba vůbec nic věděto tom, jaké je pořadí, další parametry
vyhledávání apod.
Editoval Polki (16. 4. 2022 5:04)
- Jiří Nápravník
- Člen | 710
@Polki K tomu, proč to tak mám navržené. Presenter si volá fasádu, která vrátí připravená data. V té fasádě mám potom metodu o dvou řádcích, jeden volá repozitář, který vytáhne data z databáze, a ta data na druhém řádku fasády pošlu do service, která je zpracuje a vrátí skrze fasádu do presenteru. Přijde mi to tak relativně čisté s ohledem na Single reponsibility princip. Budu rád za vyvedení z omylu.
Za ujasnění ohledně řazení díky, žil jsem v tom, že je třeba to testovat. Nakonec jsem to obešel objektem, který je listem, co má ta data seřazená, a ten samotný je otestovaný, takže by to taky mohlo být řešení.
Co se týče tvého návrhu u toho interface mi asi něco uchází, protože spolíhat na to, že to programátor implementuje tak (podle popisu), aby to bylo seřazení může být za mě zrádné… Píšeš, že to je už i UNIT testováno, ale nějak nerozumím jak, v tomto případě, můžeš mě nasměrovat?