Parametrizované Unit Testy (PUT) používame pre odhalenie chýb spôsobených nesprávnym zvládaním dátovej komplexity v programe. Týchto môže byť neúrekom, keďže častokrát je ťažké si vopred predstaviť rozmanitosť možných vstupov, hlavne ak testujeme komponent, do ktorého vniká priamy vstup od užívateľa. Viac na tematiku dátovej a štrukturálnej komplexity som sa vyjadroval v odkazovanom článku. Tu si ukážeme exemplár parametrizovaného JUnit testu triedy PostCode, ktorá z ľubovoľného String-u obsahujúceho UK poštové smerovacie číslo vyťaží informácui o distrikte. Povieme si čo-to o detailoch tohto parametrizovaného testu.
Ukážka parametrizovaného unit testu
Náš parametrizovaný unit test (PUT) má niekoľko charakterických čŕt, ktoré som v obrázku vyznačil písmenami. Teraz niekoľkými slovami ku každému z nich:
- A - testovaciu triedu okrášlime anotáciou @RunWith, aby JUnit vedel použiť správny test runner. Triedu test runneru poskytneme ako argument pre RunWith anotáciu - pre náš PUT to bude práve org.junit.runners.Parameterized
- Parameterized test runner bude v našej testovacej triede hľadať, za pomoci Java Reflection, tieto tri prvky: B - verejný konštruktor, C - verejnú statickú metódu navracajúcu kolekciu objektových polí, D - samotné parametrizované testy - ich metódy.
- C - Parameterized JUnit test runner bude hľadať verejnú statickú metódu navracajúcu kolekciu objektových polí za pomoci anotácie @Parameters, ktorou označíme metódu poskytujúcu parametre pre jednotlivé testovacie behy. Ak metóda označená @Parameters nie je nájdená - máme problém. Ak podpis metódy označenej @Parameters nespĺňa požiadavky - máme problém (Pozor, Parameterized JUnit test runner v skutočnosti požaduje návratovú hodnotu typu Collection<Object[]>. My môžeme pokojne nadefinovať návratovú hodnotu List<Object[]> alebo Stack<Object[]> - keďže sú to implementácie rozhrania Collection<Object[]>). Viac o tejto metóde ešte o pár bodov nižšie.
- Pre každé objektové pole v kolekcii navrátenej statickou metódou označenou @Parameters sa za pomocou konštruktoru B vytvorí nová inštancia testovacej triedy a všetky metódy v nej označené anotáciou @Test - D - budú zavolané štandardným spôsobom JUnit, ako sme zvyknutí z bežných (neparametrizovaných) unit testov.
- Testy označené @Test budú preberať parametre z polí - F - do ktorých si pre každý test odložíme dáta v konštruktore - B.
- Verejný konštruktor - B - bude mať typ a počet paramterov zodpovedajúci typu a počtu objektov v poli navrátenom zo statickej metódy C a je jeho povinnosťou tieto parametre z metódy C uložiť v poliach F pre použitie testom/testmi D.
Teda okrem anotácie @RunWith sa PUT líši od štandardného unit testu len konštruktorom B a statickou metódou C, ktoré spolu úzko súvisia. Parameterized JUnit test runner vytvorí novú inštanciu klasického JUnit test case-u pre každé objektové pole navrátené v kolekcii objektových polí z metódy C a spustí unit testy. Inštancie vytvorí zavolaním verejného konštruktora, ktorý vyhľadá pomocou Java Reflection na základe počtu a typu objektov v aktuálnom objektovom poli. Pozor, je povolený iba jeden konštruktor pre vašu testovaciu triedu.
@Parameters metóda a jej spojitosť s konštruktorom
Zrozumiteľne povedané - statická metóda označená anotáciou @Parameters poskytne test runneru sadu parametrov pre testy. Táto sada (kolekcia) bude pozostávať z jedného alebo viacerých objektových polí, ktoré bude tlačiť, jeden po druhom do konštruktora, ktorý musí mať typ a počet parametrov zhodujúci sa s typom a počtom objektov v poli.
Teda v našom príklade metóda data odovzdá parametrizovanému test runneru List (Arrays.asList(Object[]...)) objektových polí. Tieto polia obsahujú jednotlivé argumenty pre konštruktor testovacej triedy. Teda v príklade vidíme, že prvá inštancia bude vytvorená použitím objektového poľa { "BN1 1HG", "BN1" } a teda test runner bude hľadať konštruktor s dvoma parametrami typu String (keďže naše objektové pole obsahuje dva objekty typu String: "BN1 1HG" a "BN1"), a na nej potom JUnit vykoná všetky testy označené @Test a potom vytvorí ďalšiu inštanciu, znovu zavolaním toho istého konštruktora, ktorému podá druhé pole objektov na zozname, a tak ďalej.
Tu je poskytnutý kopírovateľný kód vyššie uvedeného príkladu:
import static org.junit.Assert.assertEquals;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class PostCodeParamTest {
private final PostCode postCode;
private final String district;
public PostCodeParamTest(String postCode, String district) {
this.postCode = new PostCode(postCode);
this.district = district;
}
@Parameters
public static Collection<Object[]> data() {
Object[][] data = {
{ "BN1 1HG", "BN1" },
{ "BN11HG", "BN1" },
{ "EC2V 7HN", "EC2V" },
{ "EC2V7HN", "EC2V" },
{ "INVALID", "" },
{ "", "" },
};
return Arrays.asList(data);
}
@Test
public void test() {
assertEquals(district, postCode.getDistrict());
}
}
|