Hlavní menu

Nástroje

SlovnikTerminologie / TestyJUnit

View (print) - Edit page | Recent changes - Page history

Updated 02 June 2013, 15:52 by JR

Testování pomocí JUnit?


1.6.2013 - Existující JUnit testy SPOTu byly z důvodu jejich neaktuálnosti odstaveny.


Take vyjmuto z me BP. (TP)

Volba řešení

Jako základní kámen pro testování aplikace jsem zvolil Spring TestContext? framework. Přestože jde o poměrně mladou součást Springu, přidána ve verzi 2.5, nebál jsem se ji použít.

JUnit?

JUnit? je framework sloužící k testování aplikací napsaných v jazyce Java. Vyvíjí ho mimo jiné Kent Beck, Erich Gamma a David Saff. Jde o open source projekt vydaný pod Common Public Licence.

Při testování se používají dva druhy tříd. Prvním jsou takzvané Suite classes, které primárně určují, které třídy s testy se mají spustit. Na třídy s testy mohou odkazovat buď přímo a nebo přes další Suite.class. A druhým druhem jsou třídy s vlastními testy.

Vlastní testování uvnitř testované metody se provádí na základě porovnávání získaných hodnot s hodnotami očekávanými. Toto porovnání nám umožnují metody frameworku z balíku org.junit.Assert. Představím některé z nich:

  • assertEquals() - metoda slouží k porovnání dvou hodnot a testuje, zda jsou stejné. Mohou se porovnávat řetězce a číselné hodnoty všech typů. Test skončí úspěšně, pokud jsou hodnoty stejné. Není doporučené pro porovnávání objektů.
  • assertFalse() - porovnává hodnotu s očekávanou hodnotou false. Pokud je testovaná hodnota false, test je úspěšný.
  • assertNotNull() - zjišťuje, zda testovaná hodnota není null. Pokud není null, test je úspěšný.
  • assertNotSame() - metoda porovnáná dva objektů. Pokud nejsou stejné, test je úspěšný.
  • assertNull() - zjišťuje, zda očekávaná hodnota je null. Pokud je, test je úspěšný.
  • assertSame() - metoda porovnává dva objekty. Pokud jsou totožné, test je úspěšný.
  • assertTrue() - porovnává testovanou hodnotu s očekávanou hodnotou true. Pokud je hodota true, test je úspěšný.
  • fail() - vyvolavá selhání testu.

Za představení jistě stojí pár základních anotací k metodám i třídám. Tyto anotace se používají namísto konvence z JUnit? 3.8.x.

  • @Before - takto oanotovaná metoda je ekvivalentem k metodě setUp() z JUnit? 3.8.x. Před spuštěním každého testu se provede kód zanesený v této metodě. Metoda se ale nemusí jmenovat setUp().
  • @BeforeClass - metoda se spustí pouze jednou před spuštěním testů. Slouží například k nastavení proměnných společných pro celou třídu.
  • @After - metoda je ekvivalntní k metodě tearDown() z JUnit? 3.8.x. Po spuštění každého testu se provede tělo metody, jde o metodu, která slouží k úklidu po testu. Metoda se ale nemusí jmenovat tearDown().
  • @AfterClass - metoda provede úklid až po proběhnutí všech testů v dané třídě.
  • @Test - takto oanotovaná metoda se nemusí jmenovat testNejakeMetody, ale může se ve jménu metody vypustit klíčové slovo test. Přestože se metoda nejmenuje podle konvencí JUnit? 3.8.x, spustí se při testování jako metoda obsahující testy.
  • @RunWith() - takto oanotovaná třída se spouští spouštěcí třídou specifikovanou v závorce.
  • @SuiteClasses(value={}) - takto oanotovaná třída spouští třídy s testy, které jsou uvedené v závorce.

Při spouštění testů přes Maven příkazem mvn test nám Maven vygeneruje v adresáři target adresář surefire-report, který obsahuje soubory s výsledky jednotlivých testovaných tříd. V nich nalezneme buď statistiku testů dané třídy a nebo chybovou hlášku v případě, že došlo k nějaké chybě.

Realizace

Spouštěcí třídou pro testy je prostá třída SpotSuiteTests.

@RunWith(Suite.class)
@SuiteClasses(value={DaoSuiteTests.class, ControllerSuiteTests.class})
public class SpotSuiteTests {	
}

Jde o základní suite třídu, která odkazuje na další suite třídy v jednotlivých podbalících aplikace. Třídy, které tato třída dále spouští, jsou uvedeny v anotaci @SuiteClasses. Jde o následující třídy DaoSuiteTests.class a ControllerSuiteTests.class. Takováto třída nemusí obsahovat žádnou metodu. Vše důležité je zaneseno v anotacích třídy.

@RunWith(Suite.class)
@SuiteClasses(value={WordDaoImplTest.class})
public class DaoSuiteTests {    
}

Toto je suite třída, ve které je nadefinována třída ke spuštění podle vzoru z předchozích řádek.

Základem tesování DAO tříd aplikace je načtení bean pomocí Inversion of Control. Toto načtení konfigurace jsem se rozhodl realizovat přes abstraktní třídu AbstractDaoTests, od které by měli dědit všechny třídy s testy v tomto balíku.

 @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:dao-context.xml"})
public abstract class AbstractDaoTests 
                extends AbstractTransactionalJUnit4SpringContextTests {

První anotace říká, že tuto třídu bude spouštět třída SpringJUnit4ClassRunner, která zajišťuje, že se třída nespustí jako běžná třída s testy, ale průběh testování bude řídit Spring framework. Další anotace definuje umístění souboru s konfigurací. Tím, že tato třída dědí od AbstractTransactionalJUnit4SpringContextTests získáváme pro naše testy funkčnost již implementovanou ve Spring frameworku. Jedná se Dependecy Injection, Inversion of Control, Transaction Managment a některé podpůrné třídy Springu.

protected Logger logger = Logger.getLogger(getClass());
protected WordDao wordDao;
protected UserDao userDao;
protected StateDao stateDao;

Definice Loggeru pro logování a dao tříd ijektovaných Springem.

 
  @Resource
  public void setWordDao(WordDao wordDao) {
      this.wordDao = wordDao;
  }

  @Resource
  public void setStateDao(StateDao stateDao) {
      this.stateDao = stateDao;
  }

  @Resource
  public void setUserDao(UserDao userDao) {
      this.userDao = userDao;
  }    
}

A potřebné settery, přes které Spring provádí injektování.

V xml souboru s konfigurací se nachází pouze beany odkazující na implementované dao třídy, beana pro načtení údajů pro připojení k databázi, takzvaná dataSource beana, beana pro načtení konfigurace z property souboru a transaction manager.

Mým úkolem bylo napsat pár testů pro jednu dao třídu, které budou sloužit jako vzor při psaní dalších testů v rámci této aplikace, a ne ji celou pokrýt testy. Proto testovaná třída má pokryto pouze pět metod.

Třídou s testy je WordDaoImplTest, která dědí od výše popsané AbstractDaoTests. Na následujícím kódu popíši část testované metody.

@Test
public void getWordsBySearch() {
    logger.info("Testing getWordsBySearch() ...");

    List<Word> wordList = wordDao.getWordsBySearch(null);
    assertNotNull(wordList);
    assertEquals(wordList.size(), 0);

    wordList = null;
    wordList = wordDao.getWordsBySearch(" ");
    assertNotNull(wordList);
    assertNotSame(wordList.size(), 0);
    assertNotSame(wordList.size(), wordDao.getAllWords().size());

    wordList = null;
    wordList = wordDao.getWordsBySearch("aabbccaabbcc");
    assertNotNull(wordList);
    assertEquals(wordList.size(), 0);
}

Do seznamu slov se přiřazují vždy výsledky hledání. První se bude vyhledávat klíčové slovo null. Předpokládá se, že tento výraz metoda v databázi nenalezne. Seznam slov se ale vytvoří s nulovým počtem položek. Proto se testuje seznam slov na assertNotNull(wordList). Dalším testem je velikost onoho seznamu. Předpokládá se nulová, proto test assertEquals(wordList.size(), 0). Po otestování prvního vyhledávání se přistoupí k testování, kdy se vyhledává ve výrazech mezera. Znovu se testuje, zda není seznam slov null. Dále seznam musí splňovat následující podmínky. Počet položek v seznamu nesmí být 0 a zároveň nemůže být roven celkovému počtu slov. Poslední krajní podmínkou je testování neexistujícího řetězce. Testuje se stejně jako u vyhledávání null.

Odkazy