Tartalomjegyzék
TDD
- Szerző: Sallai András
- Copyright © 2018, Sallai András
- Szerkesztve: 2018, 2019, 2020
- Web: https://szit.hu
A TDD
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:
- teszt kód
- ipari kód
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 TDD folyamata
A tesztelést Java objektum orientált kódokkal tekintjük meg.
Három osztályt készítünk.
- főosztályt
- teszt osztályt
- hasznos kódot tartalmazó osztály
A TDD lépései
- fejlesztjük a tesztet
- csak annyit fejlesztünk rajta, hogy az ne teljesüljön
- futtatjuk a tesztet
- fejlesztjük az ipari kódot
- csak annyit fejlesztünk, hogy teljesüljön a teszt
- futtatjuk a tesztet
- újratervezünk, ha szükséges
- tesztelünk
- folytatjuk elölről az első ponttól
Az Eclipse
Készítsünk egy projektet. A példa kedvéért legyen a neve sikidom.
- File → New → Project…
- Java → Java Project
- legyen a neve: sikidom
Készítsünk egy csomagot:
- File → New → Package
- legyen a neve: sikidom
Elkészítjük a tesztet:
- File → New → JUnit Test Case
Ha nincs a menüben a JUnit Test Case, akkor válasszuk a következőt:
- File → New → Other…
- JUnit → JUnit Test Case
- legyen a neve: HaromszogTest
Eredmény
- HaromszogTest.java
package sikidom; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; class HaromszogTest { @Test void test() { fail("Not yet implemented"); } }
A teszt fejlesztése
- HaromszogTest.java
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 futtatása
- Run → Run
A teszt hibával ér végett, mivel nem létezik a Haromszog osztály.
Az ipari kód fejlesztése
Elkészítjük a Haromszog osztályt, de nem fejlesztjük tovább.
- File → New → Class
- legyen a neve: Haromszog
- Haromszog.java
package sikidom; public class Haromszog { }
Futtatjuk a tesztet
- Run → Run
- A teszt teljesült.
- Következik a teszt továbbfejlesztése.
Teszt fejlesztése
Hívjuk meg a szamolKerulet() metódust, ami még nem létezik. Egyelőre paraméterekre sincs szüksége.
- HaormszogTest.java
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.
Ipari kód fejlesztése
Annyit fejlesszünk a kódon, hogy teljesítse a tesztet.
- Haromszog.java
package sikidom; public class Haromszog { public void szamolKerulet() { } }
Teszt fejlesztése
- HaromszogTest.java
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(); } }
Ipari kód fejlesztése
- Haromszog.java
package sikidom; public class Haromszog { public double szamolKerulet() { return 0; } }
Teszt fejlesztése
Jó lenne, ha a metódus fogadna paramétereket.
- HaromszogTest.java
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); } }
Ipari kód fejlesztése
- Haromszog.java
package sikidom; public class Haromszog { public double szamolKerulet(double a, double b, double c) { return 0; } }
Teszt fejlesztése
- HaromszogTest.java
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.
Ipari kód fejlesztése
A kódot egyszerű javítani, írjuk oda, hogy 105-tel térjen vissza.
- Haromszog.java
package sikidom; public class Haromszog { public double szamolKerulet(double a, double b, double c) { return 105; } }
A teszt lefut.
Teszt fejlesztése
- HaromszogTest.java
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.
Ipari kód fejlesztése
A kódot egyszerű javítani, írjuk oda, hogy 105-tel térjen vissza.
- Haromszog.java
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.
Újratervezés
- HaromszogTest.java
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
Ipari kód osztálya
Netbeans alatt létre kell hoznunk a tesztelendő osztályt:
- Haromszog.java
/* * 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 { }
Tesztkód
A jobb egér gombbal kattintunk a Prejects fülön a projekt nevén.
- New → Other…
- Az előugró ablakban:
- Categories: Unit Tests
- File Types: JUnit Test
- Next >
- Az újabb ablakban:
- Class Name: HaromszogTest
- Package: hu.szit.sikidom
- Generated Code:
- [ ] Test Initializer
- [ ] Test Finalizer
- [ ] Test Class Initializer
- [ ] Test Class Finalizer
- Generated Comments:
- [ ] Source Code Hints
- Finish gomb
Eredmény
- HaromszogTest.java
/* * 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() { } }
Első teszt írása
- HaromszogTest.java
/* * 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:
- Run → Test Project (sikidom)
Ipari kód fejlesztése
Írjunk annyi kódot, ami miatt a teszt nem fut hibára.
- Haromszog.java
/* * 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.
Tervezzük újra
Jelen esetben semmi különöset nem csinálok, csak kiszedem a megjegyzéseket.
- Haromszog.java
package hu.szit.sikidom; public class Haromszog { public void szamitKerulet() { } }
- HaromszogTest.java
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(); } }
A teszt fejlesztése
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.
Ipari kód fejlesztése
Éppen csak annyira fejlesztjük, hogy teljesítse a tesztet.
public class Haromszog { public double szamitKerulet() { return 0; } }
Mentsünk és futtassuk a tesztet.
Újratervezés
Most nincs mit.
Teszt fejlesztése
Jó lenne, ha metódus fogadna bemenő paramétereket.
@Test public void haromszogKeruletTest() { Haromszog har = new Haromszog(); double ker = har.szamitKerulet(30, 35, 40); }
Ipari kód fejlesztése
- Haromszog.java
package hu.szit.sikidom; public class Haromszog { public double szamitKerulet(double a, double b, double c) { return 0; } }
Újratervezés
- Haromszog.java
package hu.szit.sikidom; public class Haromszog { public double szamitKerulet(double aOldal, double bOldal, double cOldal) { return 0; } }
A teszt fejlesztése
- HaromszogTest.java
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); } }
Ipari kód fejlesztése
Ha 0 helyett 105-el térünk vissza, a teszt már teljesül:
- Haromszog.java
package hu.szit.sikidom; public class Haromszog { public double szamitKerulet(double aOldal, double bOldal, double cOldal) { return 105; } }
Újratervezés
Az alábbi sort töröljük, mivel helyettesíti az assertEquals()-t tartalmoaz sor:
double ker = har.szamitKerulet(30, 35, 40);
- HaromszogTest.java
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); } }
A teszt fejlesztése
- HaromszogTest.java
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); } }
Ipari kód fejlesztése
- Haromszog.java
package hu.szit.sikidom; public class Haromszog { public double szamitKerulet(double aOldal, double bOldal, double cOldal) { return aOldal+bOldal+cOldal; } }
Újratervezése
Most nem teszünk semmit.
Teszt fejlesztése
- HaromszogTest.java
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));
Ipari kód fejlesztése
- Haromszog.java
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; } }
Link
JUnit5 API:
Kivétel hiánya a JUnit5-ben: