Hlavní menu

Nástroje

SlovnikTerminologie / JemnyUvodDoTestoveni

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

Updated 20 February 2014, 00:12 by JR

SlovnikTerminologie.JemnyUvodDoTestoveni History

Hide minor edits - Show changes to markup

20 February 2014, 00:12 by JR -
Added line 15:
Deleted line 16:
20 February 2014, 00:11 by JR -
Added lines 15-16:
Deleted lines 17-18:
22 December 2009, 22:03 by JanSobisek a RomanKunes -
Changed lines 16-17 from:
to:
22 December 2009, 22:01 by JanSobisek a RomanKunes -
Deleted lines 3-115:

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.

Changed lines 15-17 from:
to:
31 October 2009, 18:10 by JanSobisek -
Changed lines 125-128 from:

V rámci výuky se se psaním testů setkáváme pouze sporadicky, což je dle mého názoru vcelku škoda. Má to za následek, že nejsme zvyklí testy psát a většině z nás pak tato činnost přijde naprosto zbytečná. Jakožto osoby nezasvěcené pak tvrdíme, že nám testování zabere zbytečně moc času, že to jde dobře i bez psaní testů a případně, že to prostě neumíme.

to:

V rámci výuky se se psaním testů setkáváme pouze sporadicky, což je dle mého názoru vcelku škoda. Má to za následek, že nejsme zvyklí testy psát a většině z nás pak tato činnost přijde naprosto zbytečná. Jakožto osoby nezasvěcené pak tvrdíme, že nám testování zabere zbytečně moc času, že to jde dobře i bez psaní testů a případně, že to prostě neumíme.

Odkazy

21 February 2009, 22:09 by Tomas Peterka -
Changed lines 53-54 from:
to:
Changed lines 60-80 from:

První anotace říká, že tuto třídu\footnote{A od ní odděděných tříd.} bude spouštět třída {\it 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 {\it AbstractTransactionalJUnit4SpringContextTests?} získáváme pro naše testy funkčnost již implementovanou ve Spring frameworku. Jedná se {\it Dependecy Injection, Inversion of Control, Transaction Managment} a některé podpůrné třídy Springu.\par

\begin{center} \fontsize{10pt}{1} \selectfont \begin{verbatim}

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

\end{verbatim} \end{center}

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

\begin{center} \fontsize{10pt}{1} \selectfont \begin{verbatim}

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

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;
  }
Changed lines 75-78 from:
    @Resource
    public void setStateDao(StateDao? stateDao) {
        this.stateDao = stateDao;
    }
to:
  @Resource
  public void setStateDao(StateDao? stateDao) {
      this.stateDao = stateDao;
  }
Changed lines 80-103 from:
    @Resource
    public void setUserDao(UserDao? userDao) {
        this.userDao = userDao;
    }    

} \end{verbatim} \end{center}

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

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á {\it dataSource} beana, beana pro načtení konfigurace z property souboru a transaction manager.\par

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.\par

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

\newpage

\begin{center} \fontsize{10pt}{1} \selectfont \begin{verbatim}

    @Test
    public void getWordsBySearch() {
        logger.info("Testing getWordsBySearch() ...");
to:
  @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() ...");
Changed lines 99-107 from:
        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());
to:
    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());
Changed lines 109-118 from:
        wordList = null;
        wordList = wordDao.getWordsBySearch("aabbccaabbcc");
        assertNotNull(wordList);
        assertEquals(wordList.size(), 0);
    }

\end{verbatim} \end{center}

Do seznamu slov se přiřazují vždy výsledky hledání. První se bude vyhledávat klíčové slovo {\it 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 {\it assertNotNull(wordList)}. Dalším testem je velikost onoho seznamu. Předpokládá se nulová, proto test {\it 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 {\it 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í {\it null}.

to:
    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.

21 February 2009, 22:03 by Tomas Peterka -
Changed lines 44-46 from:

\begin{center} \fontsize{10pt}{1} \selectfont \begin{verbatim}

to:

[@

Changed lines 48-59 from:

} \end{verbatim} \end{center}

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

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

\begin{center} \fontsize{10pt}{1} \selectfont \begin{verbatim}

to:

}@]

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. [@

Changed lines 58-60 from:

\end{verbatim} \end{center}

to:

@]

21 February 2009, 22:02 by Tomas Peterka -
Changed lines 29-39 from:
  • @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 {\tt mvn test} nám Maven vygeneruje v adresáři {\it target} adresář {\it 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ě. \par

\subsection{Realizace} Spouštěcí třídou pro testy je prostá třída {\it SpotSuiteTests?}.\par

\begin{center} \fontsize{10pt}{1} \selectfont \begin{verbatim}

to:
  • @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.

[@

Added lines 40-49:

}@]

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.

\begin{center} \fontsize{10pt}{1} \selectfont \begin{verbatim} @RunWith?(Suite.class) @SuiteClasses?(value={WordDaoImplTest?.class}) public class DaoSuiteTests? {

Changed lines 54-55 from:

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 {\tt @SuiteClasses?}. Jde o následující třídy {\it DaoSuiteTests?.class} a {\it ControllerSuiteTests?.class}. Takováto třída nemusí obsahovat žádnou metodu. Vše důležité je zaneseno v anotacích třídy.\par

to:

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

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

Changed lines 61-64 from:

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

to:

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

                extends AbstractTransactionalJUnit4SpringContextTests? {
Changed lines 69-72 from:

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

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

to:

První anotace říká, že tuto třídu\footnote{A od ní odděděných tříd.} bude spouštět třída {\it 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 {\it AbstractTransactionalJUnit4SpringContextTests?} získáváme pro naše testy funkčnost již implementovanou ve Spring frameworku. Jedná se {\it Dependecy Injection, Inversion of Control, Transaction Managment} a některé podpůrné třídy Springu.\par

Changed lines 74-78 from:

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

                extends AbstractTransactionalJUnit4SpringContextTests? {
to:
    protected Logger logger = Logger.getLogger(getClass());
    protected WordDao? wordDao;
    protected UserDao? userDao;
    protected StateDao? stateDao;
Changed lines 81-82 from:

První anotace říká, že tuto třídu\footnote{A od ní odděděných tříd.} bude spouštět třída {\it 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 {\it AbstractTransactionalJUnit4SpringContextTests?} získáváme pro naše testy funkčnost již implementovanou ve Spring frameworku. Jedná se {\it Dependecy Injection, Inversion of Control, Transaction Managment} a některé podpůrné třídy Springu.\par

to:

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

Deleted lines 85-96:
    protected Logger logger = Logger.getLogger(getClass());
    protected WordDao? wordDao;
    protected UserDao? userDao;
    protected StateDao? stateDao;

\end{verbatim} \end{center}

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

\begin{center} \fontsize{10pt}{1} \selectfont \begin{verbatim}

21 February 2009, 21:58 by Tomas Peterka -
Changed lines 22-33 from:

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. \par \begin{list}{}{} \item {\it @Before} - takto oanotovaná metoda je ekvivalentem k metodě {\it 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 {\it setUp()}. \item {\it @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. \item {\it @After} - metoda je ekvivalntní k metodě {\it 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 {\it tearDown()}. \item {\it @AfterClass?} - metoda provede úklid až po proběhnutí všech testů v dané třídě. \item {\it @Test} - takto oanotovaná metoda se nemusí jmenovat {\it testNejakeMetody}, ale může se ve jménu metody vypustit klíčové slovo {\it test}. Přestože se metoda nejmenuje podle konvencí JUnit? 3.8.x, spustí se při testování jako metoda obsahující testy. \item {\it @RunWith?()} - takto oanotovaná třída se spouští spouštěcí třídou specifikovanou v závorce. \item {\it @SuiteClasses?(value=\{\})} - takto oanotovaná třída spouští třídy s testy, které jsou uvedené v závorce.

\end{list}

to:

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.
21 February 2009, 21:54 by Tomas Peterka -
Changed lines 147-158 from:

Do seznamu slov se přiřazují vždy výsledky hledání. První se bude vyhledávat klíčové slovo {\it 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 {\it assertNotNull(wordList)}. Dalším testem je velikost onoho seznamu. Předpokládá se nulová, proto test {\it 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 {\it 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í {\it null}.\par

%Testování controlleru je založeno na stejném principu i strukture souboru. Proto mi přijde zbytečné tu popisovat stejné body snovu, jen s jinými názvy.\par

\subsection{Psát či nepsat testy} Rád bych věnoval pár řádků sepsání důvodů, proč bychom měli psát testy pro kód aplikace. Na testování jsou dva odlišné názory. \par První názor je, že jde o zbytečnou činnost, která zdržuje programátora od psaní vlastního kódu. \par

Druhý pohled na věc je, že programování bez testů se vývoj aplikací neobejde. Tento názor panuje hlavně tam, kde se při vývoji používají agilní metodiky jako je například Programování řízené testy a nebo Extrémní programování. V závislosti na metodice se testy píšou v různých fázích psaní kódu. U Programování řízeného testy si programátor nejdřív napíše testy na danou metodu a pak teprve vlastní metodu, kdežto u Extrémního programování programátor napíše nejdříve metodu a pak pro ni ihned píše testy. \par

Testování dle mého názoru přináší do programování zrychlení psaní aplikací. Programátor totiž nemusí ručně procházet celou aplikaci, aby zjistil, zda změnami neovlivnil některou neočekávanou funkčnost, ale stačí mu spustit automatické testy, které mu prozradí, která část kódu selhala. Rodina Xunit testů je tak široká, že se dá opravdu otestovat téměř vše a záleží na programátorovi, zda se mu chce či nikoli.\par

to:

Do seznamu slov se přiřazují vždy výsledky hledání. První se bude vyhledávat klíčové slovo {\it 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 {\it assertNotNull(wordList)}. Dalším testem je velikost onoho seznamu. Předpokládá se nulová, proto test {\it 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 {\it 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í {\it null}.

Psát či nepsat testy

Rád bych věnoval pár řádků sepsání důvodů, proč bychom měli psát testy pro kód aplikace. Na testování jsou dva odlišné názory. První názor je, že jde o zbytečnou činnost, která zdržuje programátora od psaní vlastního kódu.

Druhý pohled na věc je, že programování bez testů se vývoj aplikací neobejde. Tento názor panuje hlavně tam, kde se při vývoji používají agilní metodiky jako je například Programování řízené testy a nebo Extrémní programování. V závislosti na metodice se testy píšou v různých fázích psaní kódu. U Programování řízeného testy si programátor nejdřív napíše testy na danou metodu a pak teprve vlastní metodu, kdežto u Extrémního programování programátor napíše nejdříve metodu a pak pro ni ihned píše testy.

Testování dle mého názoru přináší do programování zrychlení psaní aplikací. Programátor totiž nemusí ručně procházet celou aplikaci, aby zjistil, zda změnami neovlivnil některou neočekávanou funkčnost, ale stačí mu spustit automatické testy, které mu prozradí, která část kódu selhala. Rodina Xunit testů je tak široká, že se dá opravdu otestovat téměř vše a záleží na programátorovi, zda se mu chce či nikoli.

21 February 2009, 21:51 by Tomas Peterka -
Changed lines 2-16 from:

Take vyjmuto z me BP. \subsection{Zadání} Zadáním bylo vytvořit infrastrukturu pro testy a sepsání návodu pro postup při psaní dalších testů. Součástí je vytvoření několika testů pro vybrané třídy. Měl jsem možnost výběru testovacího frameworku mezi TestNG? a JUnit?.\par

\subsection{Volba řešení} Jako základní kámen pro testování aplikace jsem zvolil Spring TestContext? framework. Představení vlastností Spring TestContext? Frameworku je výše v textu. 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.\par

Jelikož oba dva testovací frameworky nabízejí přibližně stejnou funkčnost a Spring TestContext? framework je podporuje oba, vybíral jsem podle svých dosavadních zkušeností. Vybral jsem tedy JUnit? řady 4.x. \par

\subsection{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.\par

Při testování se používají dva druhy tříd. Prvním jsou takzvané {\it 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ší {\it Suite.class}. A druhým druhem jsou třídy s vlastními testy. \par

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 {\tt org.junit.Assert}. Představím některé z nich: \par

to:

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. \par

Deleted lines 23-34:

\item {\it 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ů. \item {\it assertFalse()} - porovnává hodnotu s očekávanou hodnotou {\it false}. Pokud je testovaná hodnota {\it false}, test je úspěšný. \item {\it assertNotNull()} - zjišťuje, zda testovaná hodnota není {\it null}. Pokud není {\it null}, test je úspěšný. \item {\it assertNotSame()} - metoda porovnáná dva objektů. Pokud nejsou stejné, test je úspěšný. \item {\it assertNull()} - zjišťuje, zda očekávaná hodnota je {\it null}. Pokud je, test je úspěšný. \item {\it assertSame()} - metoda porovnává dva objekty. Pokud jsou totožné, test je úspěšný. \item {\it assertTrue()} - porovnává testovanou hodnotu s očekávanou hodnotou {\it true}. Pokud je hodota {\it true}, test je úspěšný. \item {\it fail()} - vyvolavá selhání testu. \end{list}

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. \par \begin{list}{}{}

21 February 2009, 21:18 by Tomas Peterka -
Added lines 1-165:

Testování

Take vyjmuto z me BP. \subsection{Zadání} Zadáním bylo vytvořit infrastrukturu pro testy a sepsání návodu pro postup při psaní dalších testů. Součástí je vytvoření několika testů pro vybrané třídy. Měl jsem možnost výběru testovacího frameworku mezi TestNG? a JUnit?.\par

\subsection{Volba řešení} Jako základní kámen pro testování aplikace jsem zvolil Spring TestContext? framework. Představení vlastností Spring TestContext? Frameworku je výše v textu. 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.\par

Jelikož oba dva testovací frameworky nabízejí přibližně stejnou funkčnost a Spring TestContext? framework je podporuje oba, vybíral jsem podle svých dosavadních zkušeností. Vybral jsem tedy JUnit? řady 4.x. \par

\subsection{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.\par

Při testování se používají dva druhy tříd. Prvním jsou takzvané {\it 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ší {\it Suite.class}. A druhým druhem jsou třídy s vlastními testy. \par

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 {\tt org.junit.Assert}. Představím některé z nich: \par \begin{list}{}{} \item {\it 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ů. \item {\it assertFalse()} - porovnává hodnotu s očekávanou hodnotou {\it false}. Pokud je testovaná hodnota {\it false}, test je úspěšný. \item {\it assertNotNull()} - zjišťuje, zda testovaná hodnota není {\it null}. Pokud není {\it null}, test je úspěšný. \item {\it assertNotSame()} - metoda porovnáná dva objektů. Pokud nejsou stejné, test je úspěšný. \item {\it assertNull()} - zjišťuje, zda očekávaná hodnota je {\it null}. Pokud je, test je úspěšný. \item {\it assertSame()} - metoda porovnává dva objekty. Pokud jsou totožné, test je úspěšný. \item {\it assertTrue()} - porovnává testovanou hodnotu s očekávanou hodnotou {\it true}. Pokud je hodota {\it true}, test je úspěšný. \item {\it fail()} - vyvolavá selhání testu. \end{list}

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. \par \begin{list}{}{} \item {\it @Before} - takto oanotovaná metoda je ekvivalentem k metodě {\it 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 {\it setUp()}. \item {\it @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. \item {\it @After} - metoda je ekvivalntní k metodě {\it 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 {\it tearDown()}. \item {\it @AfterClass?} - metoda provede úklid až po proběhnutí všech testů v dané třídě. \item {\it @Test} - takto oanotovaná metoda se nemusí jmenovat {\it testNejakeMetody}, ale může se ve jménu metody vypustit klíčové slovo {\it test}. Přestože se metoda nejmenuje podle konvencí JUnit? 3.8.x, spustí se při testování jako metoda obsahující testy. \item {\it @RunWith?()} - takto oanotovaná třída se spouští spouštěcí třídou specifikovanou v závorce. \item {\it @SuiteClasses?(value=\{\})} - takto oanotovaná třída spouští třídy s testy, které jsou uvedené v závorce.

\end{list}

Při spouštění testů přes Maven příkazem {\tt mvn test} nám Maven vygeneruje v adresáři {\it target} adresář {\it 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ě. \par

\subsection{Realizace} Spouštěcí třídou pro testy je prostá třída {\it SpotSuiteTests?}.\par

\begin{center} \fontsize{10pt}{1} \selectfont \begin{verbatim} @RunWith?(Suite.class) @SuiteClasses?(value={DaoSuiteTests?.class, ControllerSuiteTests?.class}) public class SpotSuiteTests? { } \end{verbatim} \end{center}

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 {\tt @SuiteClasses?}. Jde o následující třídy {\it DaoSuiteTests?.class} a {\it ControllerSuiteTests?.class}. Takováto třída nemusí obsahovat žádnou metodu. Vše důležité je zaneseno v anotacích třídy.\par

\begin{center} \fontsize{10pt}{1} \selectfont \begin{verbatim} @RunWith?(Suite.class) @SuiteClasses?(value={WordDaoImplTest?.class}) public class DaoSuiteTests? { } \end{verbatim} \end{center}

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

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

\begin{center} \fontsize{10pt}{1} \selectfont \begin{verbatim}

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

                extends AbstractTransactionalJUnit4SpringContextTests? {

\end{verbatim} \end{center}

První anotace říká, že tuto třídu\footnote{A od ní odděděných tříd.} bude spouštět třída {\it 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 {\it AbstractTransactionalJUnit4SpringContextTests?} získáváme pro naše testy funkčnost již implementovanou ve Spring frameworku. Jedná se {\it Dependecy Injection, Inversion of Control, Transaction Managment} a některé podpůrné třídy Springu.\par

\begin{center} \fontsize{10pt}{1} \selectfont \begin{verbatim}

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

\end{verbatim} \end{center}

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

\begin{center} \fontsize{10pt}{1} \selectfont \begin{verbatim}

    @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;
    }    

} \end{verbatim} \end{center}

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

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á {\it dataSource} beana, beana pro načtení konfigurace z property souboru a transaction manager.\par

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.\par

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

\newpage

\begin{center} \fontsize{10pt}{1} \selectfont \begin{verbatim}

    @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);
    }

\end{verbatim} \end{center}

Do seznamu slov se přiřazují vždy výsledky hledání. První se bude vyhledávat klíčové slovo {\it 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 {\it assertNotNull(wordList)}. Dalším testem je velikost onoho seznamu. Předpokládá se nulová, proto test {\it 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 {\it 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í {\it null}.\par

%Testování controlleru je založeno na stejném principu i strukture souboru. Proto mi přijde zbytečné tu popisovat stejné body snovu, jen s jinými názvy.\par

\subsection{Psát či nepsat testy} Rád bych věnoval pár řádků sepsání důvodů, proč bychom měli psát testy pro kód aplikace. Na testování jsou dva odlišné názory. \par První názor je, že jde o zbytečnou činnost, která zdržuje programátora od psaní vlastního kódu. \par

Druhý pohled na věc je, že programování bez testů se vývoj aplikací neobejde. Tento názor panuje hlavně tam, kde se při vývoji používají agilní metodiky jako je například Programování řízené testy a nebo Extrémní programování. V závislosti na metodice se testy píšou v různých fázích psaní kódu. U Programování řízeného testy si programátor nejdřív napíše testy na danou metodu a pak teprve vlastní metodu, kdežto u Extrémního programování programátor napíše nejdříve metodu a pak pro ni ihned píše testy. \par

Testování dle mého názoru přináší do programování zrychlení psaní aplikací. Programátor totiž nemusí ručně procházet celou aplikaci, aby zjistil, zda změnami neovlivnil některou neočekávanou funkčnost, ale stačí mu spustit automatické testy, které mu prozradí, která část kódu selhala. Rodina Xunit testů je tak široká, že se dá opravdu otestovat téměř vše a záleží na programátorovi, zda se mu chce či nikoli.\par

V rámci výuky se se psaním testů setkáváme pouze sporadicky, což je dle mého názoru vcelku škoda. Má to za následek, že nejsme zvyklí testy psát a většině z nás pak tato činnost přijde naprosto zbytečná. Jakožto osoby nezasvěcené pak tvrdíme, že nám testování zabere zbytečně moc času, že to jde dobře i bez psaní testů a případně, že to prostě neumíme.