Paraméterezett unit tesztek JUnit-tal

Posted by | · · · | Szoftverfejlesztés | Nincs hozzászólás a(z) Paraméterezett unit tesztek JUnit-tal bejegyzéshez

Szép tesztek. Kicsi tesztek. Olvasható tesztek.
Egy jó unit teszt leírása és magyarázata a kódunknak. Azonban ezeket az alapvető törekvéseket nem mindig sikerül betartanunk, vagy követnünk.
Egy eszközt szeretnék bemutatni, ami segíthet minket mindennapjainkban, hogy a tesztjeink kicsik, szépek és olvashatóak legyenek.
A JUnit 4-es verziójától van lehetőségünk paraméterezett tesztek írására.

A paraméterezett teszt osztály struktúrája

Akkor nevezünk egy teszt osztályt paraméterezett tesztnek, ha a @RunWith(Parameterized.class) annotációval látjuk el. Ez esetben a teszt osztálynak rendelkezni kell:

  1. egy statikus metódussal, mely teszt adatokkal tér vissza
  2. a teszt adat fogadására képes konstruktorral
  3. egy vagy több teszttel.

Továbbá a teszt adatokat generáló metódust a @Parameters annotációval kell ellátnunk és visszatérési értéke Collection<Object[]> kell hogy legyen.

Működése

A teszt osztályunk annyiszor lesz példányosítva amilyen hosszú Collection-t add vissza a teszt adat generáló metódusunk, s konstruktorunknak paraméterei egy tömb elemei lesznek. Fontos, hogy minden tömb ugyanolyan típusú elemeket tartalmazzon, s a tömbökben és a konstruktorunkban a paraméterek sorrendje megegyezzenek.

Példa

Legyen a példa problémánk egy szám prímtényezőinek meghatározása. Nevezzük ezt az osztályt PrimeFactors-nak. Legyen egy statikus metódusa generate névvel, mely egy egész számnak a prímtényezőit keresi meg, s egy listában adja ezeket vissza.

public class PrimeFactors {
    public static List<Integer> generate(int n) {...}
}

A teszteléshez példákra van szükségünk, melyek megmondják, hogy adott bemenetre milyen kimenetet várunk el. Ezeket a példákat kell beírnunk a tesztadatokat készítő metódusunka. Egy példa pár egy számokat tartalmazó lista lesz és egy szám.

Miután sikeresen meghatároztuk mit is szeretnénk csinálni, készítsük el teszt osztályunkat, egy List<Integer> és egy int privát változóval, s egy konstruktorral, mely ilyen értékeket vár, s inicializálja az osztályunk privát változóit. Végül annotáljuk teszt osztályunkat a @RunWith(Parameterized.class) annotációval, s készítsük el a tesztadatokat készítő statikus metódusunkat a @Parameters annotációval.

package com.mycompany.primefactors;
import static com.mycompany.primefactors.PrimeFactors.generate;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

@RunWith(Parameterized.class)
public class PrimeFactorsTest {

    @Parameters
    public static Collection<Object[]> testData() {
        return Arrays.asList(new Object[][]{
            {Collections.EMPTY_LIST, 1},
            {Arrays.asList(2), 2},
            {Arrays.asList(3), 3},
            {Arrays.asList(2, 2), 4},
            {Arrays.asList(5), 5},
            {Arrays.asList(2, 3), 6},
            {Arrays.asList(7), 7},
            {Arrays.asList(2, 2, 2), 8},
            {Arrays.asList(3, 3), 9},
            {Arrays.asList(3, 7), 21}
        });
    }

    private List<Integer> expectedPrimeFactors;
    private int number;

    public PrimeFactorsTest(List<Integer> expectedPrimeFactors, int number) {
        this.expectedPrimeFactors = expectedPrimeFactors;
        this.number = number;
    }

    @Test
    public void test() {
        assertEquals(expectedPrimeFactors, generate(number));
    }
}

Következtetés

Amikor paraméterezett unit tesztet kezdek el írni, akkor rá vagyok kényszerülve arra, hogy

  • pontosabban definiáljam a célt (bemeneti paraméterek, s visszatérési érték),
  • jobban átgondoljam mit és hogyan csináljak.

Módszerem pedig a következő:

  • A tesztadat generáló metódusban a lehető legegyszerűbb teszteset paramétereit adom meg.
  • Elkészítem a konstruktort, s a tesztmetódust.
  • Amikor a tesztelendő osztályom kielégítette ezt az egyszerű tesztesetet, felveszem a következőt, kicsit bonyolultabb tesztesetet.
  • És így tovább.

Az első teszteset felvétele mindig sok időt igényel, hisz ekkor definiáljuk az egész problémát, a tesztelendő osztályunk felületét. De ha sikerült ezt jól megtennünk, akkor a további tesztesetek felvétele csupán pár másodperc, legfeljebb fél perc lesz. (Ha az nem így lenne, akkor érdemes volna megfontolnunk, hogy a tesztelendő osztályunk feladatát nem tudnánk-e szétbontani kisebb egységekre) Ami így már egy igen hatékony munkamenetet eredményez. Sőt miután elkészültünk feladatunkkal, további teszteseteket pillanatok alatt előállíthatunk, tehát a teszt osztályunk nagyon könnyen kiegészíthető újabb és újabb tesztekkel minimális energia befektetéssel.

Végszó

Paraméterezett tesztek készítésekor érdemes odafigyelnünk pár apróságra:

  • Használjunk beszédes nevű konstansokat.
  • Törekedjünk a néhány soros teszt metódusokra.
Ezek eredményeképpen könnyen olvasható teszteket kaphatunk.


No Comments

Leave a comment