A TDD a Test-driven development rövidítése, magyarul teszt-vezérelt fejlesztés. Egy szoftverfejlesztési folyamat, amely nagyon rövid fejlesztési ciklusok ismétléséből áll. Kent Beck amerikai mérnök alkotta meg, vagy újra felfedezte.
Tulajdonképpen a kód egy részét, ami jellemzően egy függvény, eljárás vagy metódus, teszteljük, újra és újra. A vizsgált részt egy egységként vagy angolul unitként szokás emlegetni. Így egységteszt vagy unit tesztnek is hívják.
Két kódot fejlesztünk párhuzamosan:
A fejlesztést a teszt megírásával kezdjük, akkor amikor még nem is áll rendelkezésre az ipari kód.
A mai integrált fejlesztői környezetek támogatják a tesztek írását. Így könnyedén használhatjuk a teszt-vezérelt fejlesztésre.
A tesztelést Java objektum orientált kódokkal tekintjük meg.
Három osztályt készítünk.
A TDD lépései
Készítsünk egy projektet. A példa kedvéért legyen a neve sikidom.
Készítsünk egy csomagot:
Elkészítjük a tesztet:
Ha nincs a menüben a JUnit Test Case, akkor válasszuk a következőt:
package sikidom; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; class HaromszogTest { @Test void test() { fail("Not yet implemented"); } }
package sikidom; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; class HaromszogTest { @Test void test() { Haromszog haromszog = new Haromszog(); } }
A teszt hibával ér végett, mivel nem létezik a Haromszog osztály.
Elkészítjük a Haromszog osztályt, de nem fejlesztjük tovább.
package sikidom; public class Haromszog { }
Hívjuk meg a szamolKerulet() metódust, ami még nem létezik. Egyelőre paraméterekre sincs szüksége.
package sikidom; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; class HaromszogTest { @Test void test() { Haromszog haromszog = new Haromszog(); haromszog.szamolKerulet(); } }
Futtassuk a tesztet. A teszt nem teljesül.
Annyit fejlesszünk a kódon, hogy teljesítse a tesztet.
package sikidom; public class Haromszog { public void szamolKerulet() { } }
package sikidom; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; class HaromszogTest { @Test void test() { Haromszog haromszog = new Haromszog(); double ker = haromszog.szamolKerulet(); } }
package sikidom; public class Haromszog { public double szamolKerulet() { return 0; } }
Jó lenne, ha a metódus fogadna paramétereket.
package sikidom; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; class HaromszogTest { @Test void test() { Haromszog haromszog = new Haromszog(); double ker = haromszog.szamolKerulet(30, 35, 40); } }
package sikidom; public class Haromszog { public double szamolKerulet(double a, double b, double c) { return 0; } }
package sikidom; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; class HaromszogTest { @Test void test() { Haromszog haromszog = new Haromszog(); double ker = haromszog.szamolKerulet(30, 35, 40); assertEquals(105, haromszog.szamolKerulet(30, 35, 40)); } }
A teszt sikertelen. Hiba ugyan nincs, de a teszt sikertelen, mivel nem 105 az eredményt, 30, 35 és 40 bemenő paraméterek esetén.
A kódot egyszerű javítani, írjuk oda, hogy 105-tel térjen vissza.
package sikidom; public class Haromszog { public double szamolKerulet(double a, double b, double c) { return 105; } }
A teszt lefut.
package sikidom; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; class HaromszogTest { @Test void test() { Haromszog haromszog = new Haromszog(); double ker = haromszog.szamolKerulet(30, 35, 40); assertEquals(105, haromszog.szamolKerulet(30, 35, 40)); assertEquals(75, haromszog.szamolKerulet(20, 25, 30)); } }
A teszt újra sikertelen. A hiba csak 30, 35 és 40 bemenő paraméterek esetén működik.
A kódot egyszerű javítani, írjuk oda, hogy 105-tel térjen vissza.
package sikidom; public class Haromszog { public double szamolKerulet(double a, double b, double c) { return a + b + c; } }
A teszt lefut.
Ezek után jöhet a tesztelés szélsőértékekre.
package sikidom; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; class HaromszogTest { @Test void szamolKeruletTest() { Haromszog haromszog = new Haromszog(); assertEquals(105, haromszog.szamolKerulet(30, 35, 40)); assertEquals(75, haromszog.szamolKerulet(20, 25, 30)); } }
Kitöröltük a felesleges sort, mivel mindenképpen meghívjuk szamolKerulet() metódust. Mivel a test() metódus csak a szamolKerulet() metódust teszteli, ezért átnevezzük szamolKeruletTest() névre.
Netbeans alatt létre kell hoznunk a tesztelendő osztályt:
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package hu.szit.sikidom; /** * * @author andras */ public class Haromszog { }
A jobb egér gombbal kattintunk a Prejects fülön a projekt nevén.
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package hu.szit.sikidom; import org.junit.Test; import static org.junit.Assert.*; /** * * @author andras */ public class HaromszogTest { public HaromszogTest() { } }
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ import org.junit.Test; import static org.junit.Assert.*; /** * * @author andras */ public class HaromszogTest { public HaromszogTest() { } @Test public void test() { Haromszog har = new Haromszog(); har.szamitKerulet(); } }
Futtassuk a tesztet:
Írjunk annyi kódot, ami miatt a teszt nem fut hibára.
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package hu.szit.sikidom; /** * * @author andras */ public class Haromszog { public void szamitKerulet() { } }
A teszt most már nem fut hibára.
Jelen esetben semmi különöset nem csinálok, csak kiszedem a megjegyzéseket.
package hu.szit.sikidom; public class Haromszog { public void szamitKerulet() { } }
package hu.szit.sikidom; import org.junit.Test; import static org.junit.Assert.*; public class HaromszogTest { public HaromszogTest() { } @Test public void haromszogKeruletTest() { Haromszog har = new Haromszog(); har.szamitKerulet(); } }
Jó lenne, ha adna vissza értéket szamitKerulet() metódus.
@Test public void haromszogKeruletTest() { Haromszog har = new Haromszog(); double ker = har.szamitKerulet(); }
Futtassuk a tesztet.
Éppen csak annyira fejlesztjük, hogy teljesítse a tesztet.
public class Haromszog { public double szamitKerulet() { return 0; } }
Mentsünk és futtassuk a tesztet.
Most nincs mit.
Jó lenne, ha metódus fogadna bemenő paramétereket.
@Test public void haromszogKeruletTest() { Haromszog har = new Haromszog(); double ker = har.szamitKerulet(30, 35, 40); }
package hu.szit.sikidom; public class Haromszog { public double szamitKerulet(double a, double b, double c) { return 0; } }
package hu.szit.sikidom; public class Haromszog { public double szamitKerulet(double aOldal, double bOldal, double cOldal) { return 0; } }
package hu.szit.sikidom; import org.junit.Test; import static org.junit.Assert.*; public class HaromszogTest { public HaromszogTest() { } @Test public void haromszogKeruletTest() { Haromszog har = new Haromszog(); double ker = har.szamitKerulet(30, 35, 40); assertEquals(105, har.szamitKerulet(30, 35, 40), 0); } }
Ha 0 helyett 105-el térünk vissza, a teszt már teljesül:
package hu.szit.sikidom; public class Haromszog { public double szamitKerulet(double aOldal, double bOldal, double cOldal) { return 105; } }
Az alábbi sort töröljük, mivel helyettesíti az assertEquals()-t tartalmoaz sor:
double ker = har.szamitKerulet(30, 35, 40);
package hu.szit.sikidom; import org.junit.Test; import static org.junit.Assert.*; public class HaromszogTest { public HaromszogTest() { } @Test public void haromszogKeruletTest() { Haromszog har = new Haromszog(); assertEquals(105, har.szamitKerulet(30, 35, 40), 0); } }
package hu.szit.sikidom; import org.junit.Test; import static org.junit.Assert.*; public class HaromszogTest { public HaromszogTest() { } @Test public void haromszogKeruletTest() { Haromszog har = new Haromszog(); assertEquals(105, har.szamitKerulet(30, 35, 40), 0); assertEquals(75, har.szamitKerulet(20, 25, 30), 0); } }
package hu.szit.sikidom; public class Haromszog { public double szamitKerulet(double aOldal, double bOldal, double cOldal) { return aOldal+bOldal+cOldal; } }
Most nem teszünk semmit.
package hu.szit.sikidom; import org.junit.Test; import static org.junit.Assert.*; public class HaromszogTest { public HaromszogTest() { } @Test public void haromszogKeruletTest() { Haromszog har = new Haromszog(); assertEquals(105, har.szamitKerulet(30, 35, 40), 0); assertEquals(75, har.szamitKerulet(20, 25, 30), 0); } @Test(expected = IllegalArgumentException.class) public void haromszogKeruletException() { Haromszog har = new Haromszog(); har.szamitKerulet(0, 25, 30); } }
JUnit5 már lehetővé teszi:
assertThrows(IllegalArgumentException.class, har.szamitKerulet(0, 25, 30));
package hu.szit.sikidom; public class Haromszog { public double szamitKerulet(double aOldal, double bOldal, double cOldal) { if(aOldal <= 0) { throw new IllegalArgumentException("Nem megfelelő paraméter"); } return aOldal+bOldal+cOldal; } }
JUnit5 API:
Kivétel hiánya a JUnit5-ben: