it-swarm-eu.dev

Jak napsat „dobré“ testy jednotek?

Spuštěno toto vlákno , já (znovu) přemýšlím o konečném použití jednotkových testů v mých projektech. Několik plakátů tam říká něco jako "Testy jsou v pohodě, pokud jsou dobré testy". Moje otázka: Jaké jsou „dobré“ testy?

V mých aplikacích je hlavní částí často nějaký druh numerické analýzy, v závislosti na velkém množství pozorovaných dat, a výsledkem je funkce fit, kterou lze použít k modelování těchto dat. Zjistil jsem, že je obzvláště obtížné konstruovat testy pro tyto metody, protože počet možných vstupů a výsledků je příliš velký na to, aby se testoval každý případ, a samotné metody jsou často poměrně dlouhé a nelze je snadno refaktorizovat bez obětování výkonu. Zejména mě zajímají „dobré“ testy pro tento druh metody.

63
Jens

The Art of Unit Testing má co říci o jednotkových testech:

Jednotkový test by měl mít následující vlastnosti:

  • Měl by být automatizovaný a opakovatelný.
  • Realizace by měla být snadná.
  • Jakmile je napsán, měl by zůstat pro budoucí použití.
  • Každý by to měl být schopen spustit.
  • Měl by běžet stiskem tlačítka.
  • Mělo by to běžet rychle.

a později přidá, že by měl být plně automatizovaný, důvěryhodný, čitelný a udržovatelný.

Důrazně doporučujeme přečíst tuto knihu, pokud jste tak již neučinili.

Podle mého názoru jsou to všechno velmi důležité, ale zejména poslední tři (důvěryhodné, čitelné a udržovatelné), jako by vaše testy měly tyto tři vlastnosti, pak je obvykle váš kód také obsahuje.

52
Andy Lowry

Dobrý test jednotky neodráží funkci, kterou testuje.

Jako velmi zjednodušený příklad zvažte, že máte funkci, která vrací v průměru dvě int. Nejkomplexnější test by zavolal funkci a zkontroloval, zda je výsledek ve skutečnosti průměr. To nedává vůbec žádný smysl: zrcadlíte (replikujete) funkčnost, kterou testujete. Pokud jste udělali chybu v hlavní funkci, uděláte stejnou chybu v testu.

Jinými slovy, pokud zjistíte, že replikujete hlavní funkce v testu jednotky, je to pravděpodobně známka toho, že ztrácíte čas.

43
mojuba

Dobré jednotkové testy jsou v podstatě specifikací ve spustitelné formě:

  1. popsat chování kódu odpovídající případům použití
  2. pokrývat případy technických rohů (co se stane, když je předán nulový test) - pokud není test pro rohový případ přítomen, chování není definováno.
  3. break, pokud se testovaný kód změní mimo specifikaci

Zjistil jsem, že Test-Driven-Development je velmi vhodný pro knihovní rutiny, protože v podstatě napíšete API nejprve a poté skutečnou implementaci.

10
user1249

u TDD funkce „dobrých“ testů , které zákazník požaduje ; funkce nemusí nutně odpovídat funkcím a testovací scénáře by vývojář neměl vytvářet ve vakuu

ve vašem případě - hádám - „vlastnost“ je, že funkce fit modeluje vstupní data v určité toleranci chyb. Protože nevím, co opravdu děláte, něco vymýšlím; doufejme, že je analgózní.

Příklad příběhu:

Jako [X-Wing Pilot] chci [ne více než 0,0001% fit fit], aby [zaměřovací počítač mohl zasáhnout výfukový port Death Star, když se pohyboval plnou rychlostí skrz kaňon krabice]

Takže jdete mluvit s piloty (a se zaměřovacím počítačem, je-li vnímavý). Nejprve si povídáte o tom, co je „normální“, a potom o abnormálním. Zjistíte, na čem v tomto scénáři skutečně záleží, co je běžné, co je nepravděpodobné a co je pouze možné.

Řekněme, že za normálních okolností budete mít půlsekundové okno na sedmi kanálech telemetrických dat: rychlost, stoupání, svitek, vybočení, cílový vektor, velikost cíle a rychlost cíle a že tyto hodnoty budou konstantní nebo lineárně se mění. Za normálních okolností můžete mít méně kanálů a/nebo se hodnoty mohou rychle měnit. Takže společně přijdete s několika testy, jako například:

//Scenario 1 - can you hit the side of a barn?
Given:
    all 7 channels with no dropouts for the full half-second window,
When:
    speed is zero
    and target velocity is zero
    and all other values are constant,
Then:
    the error coefficient must be zero

//Scenario 2 - can you hit a turtle?
Given:
    all 7 channels with no dropouts for the full half-second window,
When:
    speed is zero
    and target velocity is less than c
    and all other values are constant,
Then:
    the error coefficient must be less than 0.0000000001/ns

...

//Scenario 42 - death blossom
Given:
    all 7 channels with 30% dropout and a 0.05 second sampling window
When:
    speed is zero
    and position is within enemy cluster
    and all targets are stationary
Then:
    the error coefficient must be less than 0.000001/ns for each target

Nyní jste si možná všimli, že pro situaci popsanou v příběhu neexistuje scénář. Ukázalo se, že po rozhovoru se zákazníkem a dalšími zúčastněnými stranami byl tento cíl v původním příběhu pouze hypotetickým příkladem. Skutečné testy vyšly z následující diskuse. To se může stát. Příběh by měl být přepsán, ale nemusí to být [protože příběh je jen zástupný symbol pro rozhovor se zákazníkem].

7
Steven A. Lowe

Vytvořte testy pro rohové případy, jako je testovací sada obsahující pouze minimální počet vstupů (možný 1 nebo 0) a několik standardních případů. Tyto jednotkové testy nejsou náhradou za důkladné akceptační testy a neměly by být.

5
user281377

Viděl jsem spoustu případů, kdy lidé investují obrovské množství pokusů o psaní kódu pro kód, který se zadává jen zřídka, a nepíšu testy pro kód, který se zadává často.

Než se posadíte, abyste mohli napsat jakékoli testy, měli byste se podívat na nějaký graf volání, abyste se ujistili, že máte dostatečné pokrytí.

Navíc nevěřím v psaní testů jen proto, abych řekl: „Ano, testujeme to“. Používám-li knihovnu, která je zasunutá a zůstane neměnná, nebudu plýtvat denními testy, abych se ujistil, že vnitřky API, které se nikdy nezmění, fungují podle očekávání, i když určité části skóre mají skóre vysoko na grafu hovorů. Testy, na které poukazují konzumují uvedená knihovna (můj vlastní kód).

5
Tim Post

Ne tak docela TDD, ale poté, co jste šli do QA, můžete své testy vylepšit nastavením testovacích případů k reprodukci jakýchkoli chyb, které se objeví během procesu QA. To může být zvláště užitečné, když se chystáte na dlouhodobější podporu a začnete se dostávat na místo, kde riskujete, že lidé neúmyslně znovu zavedou staré chyby. Mít test na zachycení, který je zvláště cenný.

4
glenatron

Snažím se nechat každý test otestovat pouze jednu věc. Snažím se každému testu dát jméno jako shouldDoSomething (). Snažím se otestovat chování, ne implementaci. Testuji pouze veřejné metody.

Obvykle mám jeden nebo několik testů na úspěch a pak možná několik testů na selhání, na veřejnou metodu.

Mock-upy používám hodně. Dobrá simulovaná struktura by pravděpodobně byla docela užitečná, například PowerMock. I když ještě nepoužívám.

Pokud třída A používá jinou třídu B, přidal bych rozhraní X, takže A nepoužívá B přímo. Pak bych vytvořil maketu XMockup a použil ji místo B ve svých testech. Skutečně to pomáhá urychlit provádění testu, snižuje složitost testu a také snižuje počet testů, které píšu pro A, protože se nemusím vypořádat s zvláštnostmi B. Mohu například vyzkoušet, že A volá X.someMethod () místo vedlejšího efektu volání B.someMethod ().

Udržujte si i testovací kód čistý.

Při použití API, jako je například databázová vrstva, bych ji zesměšňoval a umožňoval by mock-upu vyvolávat výjimku při každé možné příležitosti na příkaz. Poté jsem provedl testy bez házení a ve smyčce, pokaždé, když při další příležitosti házel výjimku, dokud test znovu nepokračoval. Trochu jako testy paměti dostupné pro Symbian.

3

Vidím, že Andry Lowry již zveřejnil metriku Roy Osherove v testech jednotek; ale zdá se, že nikdo nepředložil (doplňkový) soubor, který strýček Bob dává Čistý kód (132-133). Používá zkratku FIRST (zde se svými shrnutími):

  • Rychle (měli by běžet rychle, takže lidem nebude vadit jejich spouštění)
  • Nezávisle (testy by si neměly navzájem nastavovat nebo ničit)
  • opakovatelné (mělo by běžet ve všech prostředích/platformách)
  • Self-validating (plně automatizovaný; výstup by měl být buď "pass" nebo "fail", nikoli soubor protokolu)
  • Včasné (kdy je napsat - těsně před napsáním výrobního kódu, který testují)
2
Kazark