Testarea unitară s-a impus în ultima perioadă în dezvoltarea proiectelor scrise în limbajul Java și numai, pe măsura apariției unor utilitare gratuite de testare a claselor, care au contribuit la creșterea vitezei de programare și la micșorarea drastică a numărului de bug-uri.
Cel mai folosit utilitar pentru testarea unitară a claselor Java este JUnit, care se
poate descărca gratuit de pe site-ul http://www.junit.org . Arhiva este destul de mică
și include un director (junitxxx
) cu documentație (în directorul
doc), documentația API (în directorul javadoc), biblioteca de clase junit.jar
și exemple
de clase de test (în directorul junit).
Printre avantajele folosirii utilitarului JUnit se numără:
JUnit a fost proiectat pe baza a două modele (patterns): modelul Command si modelul Composite.
O clasa TestCase este un obiect command și orice clasă ce conține metode de test
trebuie sa subclaseze clasa TestCase
. O clasa TestCase
se compune dintr-un
numar de metode publice testXXX()
. Pentru a verifica rezultatele așteptate și cele
curente se va invoca una dintre metodele assert()
.
Metodele setUp()
si tearDown()
servesc la initializarea si distrugerea oricărui obiect
utilizat în cadrul testelor. Fiecare test rulează într-un context propriu și apelează
metoda setUp()
înainte și metoda tearDown()
după fiecare metodă de test pentru a
evita efectele secundare dintre teste.
Instanțele TestCase
se pot compune sub forma unor ierarhii TestSuite
ce vor
invoca automat toate metodele testXXX()
definite în fiecare instanță TestCase
. O
clasă TestSuite
poate fi compusă din alte teste, instanțe TestCase
sau alte instanțe
TestSuite
.
Diagrama de clase a pachetului junit.framework
este următoarea:
Să considerăm ca exemplu o aplicație de tip librărie virtuală, în care se definește o
clasă Book
ce păstrează informații despre cărțile din librărie și o clasă de tip coș de
cumpărături ShoppingCart
ce păstrează lista cărților pe care un client dorește să le
cumpere.
Codul sursă al celor două clase este prezentat mai jos:
Book.java
/** * Implement a Book. * *@created 04 aprilie 2005 */ public class Book { /** * The book title. */ private String title; /** * The book author. */ private String author; /** * The book price. */ private double price; /** * Constructor pentru |
ShoppingCart.java
import java.util.*; /** * Implement an shopping cart. * *@created 04 aprilie 2005 */ public class ShoppingCart { /** * The items list. */ private List items; /** * The |
Etapele creării unei clase de test sunt:
TestCase
;
setUp()
pentru inițializarea obiectelor testate
tearDown()
pentru eliberarea obiectelor testate
testXXX()
ce testează obiectele
și anticipează rezultatele așteptate
suite()
de tip factory ce creează o suită
TestSuite
care conține toate metodele testXXX()
din subclasa clasei
TestCase
main()
ce returnează subclasa TestCase
în mod
batch
Un exemplu de clasă de test pentru coșul de cumparături este prezentat mai jos:
ShoppingCartTest.java
import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; /** * Clasa |
Se observă că clasa ShoppingCartTest
este extinsă din clasa TestCase
. S-a
inițializat în metoda setUp()
un obiect bookCart
de clasă ShoppingCart
și un
obiect book
de clasă Book, care a fost adăugat obiectului bookCart
.
În metoda testBookAdd()
s-a testat adăugarea unei cărți în coș folosind metoda
assertEquals(double expected, double actual, double delta)
,
delta fiind diferenta acceptată pentru comparații intre două valori de tip double,
precum și incrementarea numărătorului de itemuri din coș cu metoda
assertEquals(int expected, int actual)
Metoda testEmpty()
testează golirea coșului după apelul metodei empty()
. S-a
utilizat metoda assertTrue(boolean condition)
care afișează un mesaj de
eroare în cazul în care condiția este falsă.
Metoda testBookRemove()
testează operația de eliminare a unui obiect Book din
coșul bookCart
. Coșul are inițial un obiect Book
(inserat prin metoda setUp()
)
care este eliminat din coș cu metoda removeItem()
. Se testează ca numărul de
obiecte Book
rămase să fie zero.
Metoda testBookNotFound()
testează inexistența in coș a unui obiect Book
care
nu a fost adăugat anterior, în caz de eroare se aruncă o excepție prin apelul metodei
fail()
.
Pentru testare se adaugă la CLASSPATH
calea către biblioteca junit.jar
și către
directorul unde se află clasele sursă, se compilează toate cele trei clase si se
lansează în execuție cu comanda:
java ShoppingCartTest
(din directorul claselor)
Rezultatul la consolă este prezentat în imaginea următoare:
JUnit furnizează atât o interfață text pentru utilizator, cât și o interfață grafică, fiecare indicând numarul de teste, erorile și starea finală a testului.
Interfata text (junit.textui.TestRunner
) afișează câte un punct pentru fiecare
test efectuat și un OK dacă toate testele au fost trecute cu succes sau mesaje de
eroare dacă unul dintre teste a eșuat.
Interfata grafică (junit.swingui.TestRunner
) afișează o fereastră de tip Swing
cu un bară verde dacă toate testele au trecut cu succes sau o bară roșie dacă unul
sau mai multe teste eșuează.
Interfața grafică este specificată în metoda main()
.
Pentru a afișa rezultatele testelor folosind interfața grafică, în metoda main()
în loc
de
junit.textui.TestRunner.run(suite());
se scrie
junit.swingui.TestRunner.run(ShoppingCartTest.class);
Se recompilează clasa de test si se lansează în execuție, rezultatul afișat fiind prezentat în imaginea următoare:
Dacă un anumit test eșuează (de exemplu operația de eliminare a unui obiect Book
din ShoppingCart
), atunci se va afișa un mesaj de eroare de forma:
Metodele assertXXX()
utilizate la scrierea testelor sunt prezentate în tabelul de mai
jos:
assertEquals(primitive expected, primitive actual)
|
Verifică dacă cele două obiecte primitive sunt egale |
assertEquals(Object expected,Object actual)
|
Verifică dacă cele două obiecte sunt egale (folosind metoda equals() din Object )
|
assertSame(Object expected,Object actual)
|
Verifică dacă cele două obiecte au aceeași adresă de memorie |
assertNotSame(Object expected,Object actual)
|
Verifică dacă cele două obiecte nu sunt unul și același (nu au aceeași adresă de memorie) |
assertNull(Object object)
|
Verifică dacă un obiect este null |
assertNotNull(Object object)
|
Verifică dacă un obiect nu este null |
assertTrue(boolean condition)
|
Verifică dacă o condiție este adevărată |
assertFalse(boolean condition)
|
Verifică dacă o condiție nu este adevărată |
Se pot crea suite de test ce include o ierarhie de clase de test sau alte suite. Etapele creării unei suite de test sunt:
Un exemplu de clasă suită de test este următorul:
AllTests.java
import junit.framework.Test; import junit.framework.TestSuite; /** * Run all tests. * *@author Anonymous *@created 04 aprilie 2005 */ public class AllTests { /** * Asambleaza si returneaza o suita de teste Se pot adauga teste noi. * *@return O suita de teste. */ public static Test suite() { TestSuite suite = new TestSuite(); // clasa ShoppingCartTest. suite.addTest(ShoppingCartTest.suite()); // O alta clasa de test. //suite.addTest(CreditCardTest.suite()); return suite; } /** * Lanseaza in executie suita de teste. * *@param args Description of the Parameter */ public static void main(String args[]) { junit.swingui.TestRunner.run(AllTests.class); } } |
Pentru testarea suitei se compilează toate clasele și se lansează comanda
java AllTests
Dacă proiectul de implementat este construit folosind utilitarul Ant
(http://ant.apache.org/), atunci în fișierul build.xml
se pot insera teste unitare.
Să presupunem că sursele se află în directorul src
, clasele generate în directorul classes
,
iar biblioteca junit.jar
se află în directorul lib
al proiectului. Se pot compila sursele
Java și se pot rula testele folosind fișierul build.xml
, plasat în directorul rădăcină al
proiectului. Biblioteca junit.jar
trebuie să se găsească în directorul lib
al directorului
unde este instalat Ant.
Conținutul fișierului build.xml
este prezentat mai jos:
build.xml
<?xml version="1.0" encoding="iso-8859-1"?> <project name="sample-junit" default="all" basedir="."> <description>Build Sample JUnit project</description> <property name="src" value="src"/> <property name="lib" value="lib"/> <property name="tmp" value="tmp"/> <path id="cp"> <fileset dir="${lib}" includes="*.jar"/> <pathelement path="${tmp}"/> </path> <target name="compile" description="Compile Java source files"> <javac srcdir="${src}" destdir="${tmp}" debug="on" deprecation="on" classpathref="cp"/> </target> <target name="test" depends="compile" description="Run JUnit tests"> <junit haltonfailure="false" haltonerror="false" printsummary="withOutAndErr"> <classpath refid="cp"/> <batchtest> <fileset dir="${src}" includes="**/*Test*.java"/> </batchtest> </junit> </target> <target name="clean" description="Clean generated files"> <delete dir="${tmp}"/> <mkdir dir="${tmp}"/> </target> <target name="all" depends="clean,test" description="Build the whole project"/> </project> |
Ant poate afișa rapoarte de test în format HTML. Pentru aceasta trebuie înlocuit targetul de test cu cel de mai jos:
<target name="test" depends="compile" description="Run JUnit tests"> <junit haltonfailure="false" printsummary="withOutAndErr"> <classpath refid="cp"/> <batchtest todir="${tmp}"> <fileset dir="${src}" includes="**/*Test*.java"/> </batchtest> <formatter type="xml"/> </junit> <junitreport todir="${tmp}"> <fileset dir="${tmp}" includes="TEST-*.xml"/> <report format="frames" todir="${tmp}"/> </junitreport> |
Ant va genera un raport XML în directorul tmp, iar elementul junitreport va genera un raport HTML din fișierele XML. Elementul junitreport necesită instalarea bibliotectii Xalan versiunea 2 în directorul lib al instanței Ant. Raportul generat arată ca în imaginea următoare:
Acest gen de raport este util dacă se rulează un număr mare de teste simultan.
Câteva recomandări cu privire la organizarea și execuția testelor:
JUnit este un utilitar valoros oricărui programator Java, permițând creșterea vitezei de programare și depistarea bug-urilor încă în faza de implementare a codului sursă.
Mai multe unelte de dezvoltare pentru Java (cum ar fi JBuilder sau Eclipse) au încorporat suport pentru JUnit și Ant.
Pentru mai multe informații vă invit sa vizitați site-ul web http://www.junit.org ce conține multe articole și documentație despre JUnit. Cei care lucrează și cu alte limbaje de programare pot folosi alte utilitare de testare unitară ce se pot descărca gratuit de pe Internet: