Salta el contingut
 

JUnit

Autor: Joan Puigcerver Ibáñez

Correu electrònic: j.puigcerveribanez@edu.gva.es

Llicència: CC BY-NC-SA 4.0

(Reconeixement - NoComercial - CompartirIgual) 🅭

JUnit

JUnit és una llibreria de Java que proporciona un marc per escriure i executar proves de programari.

Instal·lació de JUnit mitjançant Maven

Per utilitzar JUnit, primer hauràs de instal·lar la llibreria al teu projecte.

Documentació

Documentació oficial IntelliJ: Get started with JUnit

Podem instal·lar la llibreria JUnit mitjançant Maven.

Maven és una eina de gestió de projectes que es basa en un conjunt de normes que defineixen com s'han de construir i empaquetar els projectes, així com les dependències amb altres projectes o llibreries.

Això fa que siga molt més senzill de gestionar projectes grans, ja que es pot utilitzar Maven per crear fàcilment una estructura de directoris comuna i automatitzar tasques com la compilació, validació i empaquetat.

Per poder utilitzar JUnit al nostre projecte, hem d'afegir la llibreria com una dependència:

  1. Obri el fitxer pom.xml que es troba a l'arrel del teu projecte.
  2. Una vegada obert, prem Alt+Insert i selecciona Add dependency.

    Tip

    El cursor ha d'estar dins de l'etiqueta <project>.

    Afegir dependència a Maven

    Figura 1. Afegir dependència a Maven

  3. A la finestra, escriu org.junit.jupiter:junit-jupiter a la barra de cerca.

    Selecciona la dependència necessària i prem Add per afegir-la.

    Note

    La versió de JUnit en el moment de la creació d'aquesta guia és la 5.10.0.

    No obstant això, la versió pot variar en el futur.

    Afegir dependència JUnit a Maven

    Figura 2. Afegir dependència JUnit a Maven

    Aquesta acció afegirà al fitxer de configuració de Maven pom.xml un text semblant a:

    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.10.0</version>
        </dependency>
    </dependencies>
    
  4. Ara hem d'aplicar els canvis realitzats a la configuració de Maven. Premeu Ctrl+Maj+O o feu clic a Load Maven Changes a la notificació que apareix a l'extrem superior dret de l'editor.

    Fitxer de configuració Maven actualitzat

    Figura 3. Fitxer de configuració Maven amb la dependència JUnit

    Després de actualitzar, hauria de quedar aixina:

    Fitxer de configuració Maven actualitzat

    Figura 4. Fitxer de configuració Maven actualitzat

Creació de les proves

En un projecte Maven, les proves s'han de guardar en la carpeta src/test/java.

Podeu crear els fitxers de les proves manualment o utilitzar les eines de l'IDE.

Execució de les proves

Les proves poden ser executades de la mateixa manera que s'executa el codi font amb el botó Run o Debug.

Assert

En Java, la instrucció assert és una instrucció que s'utilitza per comprovar que una determinada condició és certa durant l'execució d'una prova unitària.

Si la condició no es compleix, la prova llançarà una excepció (error) del tipus AssertionError.

En JUnit, hi ha molts tipus diferents d'instruccions assert disponibles per fer verificacions diferents durant l'execució de proves unitàries.

assertEquals

La instrucció assertEquals(expected, actual) s'utilitza per comprovar que dos valor són iguals, on expected és el valor que s'espera i actual és el valor que has obtingut.

Si expected i actual són iguals, la prova continuarà executant-se. Si no ho són, la prova fallarà amb una AssertionError.

Important

Aquest mètode utilitza el mètode equals() dels objectes per determinar si els objectes són iguals o no.

assertEquals(10, 3 + 7), // Integer
assertEquals('b', 'a' + 1), // Char
assertEquals(1.5, 3.0 / 2), // Double
assertEquals(1.5, 3.1 / 2, 0.1), // Double with tolerance (delta)
assertEquals("HELLO", "hello".toUpperCase()) // String

En el cas de dels tipus double i float, es pot definir una tolerància (delta):

assertEquals(1.5, 3.1 / 2, 0.1), // Double with tolerance (delta)

assertTrue i assertFalse

La instrucció assertTrue(expression) i assertFalse(expression) s'utilitza per comprovar que una expressió es vertadera o falsa, respectivament. Si no ho és, la prova fallarà.

assertTrue(isPositive(5));
assertFalse(isPositive(-1));

assertNull i assertNotNull

La instrucció assertNull(obj) i assertNotNull(obj) s'utilitza per comprovar si un objecte és null o no, respectivament. Si no té el valor corresponent, la prova fallarà.

String a = null;
String b = "Joan";
assertNull(a);
assertNotNull(b);

assertSame i assertNotSame

La instrucció assertSame(obj1, obj2) i assertNotSame(obj1, obj2) s'utilitza per comprovar si dos objectes son el mateix o no, respectivament. Si no compleixen la condició, la prova fallarà.

Important

Aquest mètode utilitza l'operador ==, que en els objectes comprova si exactament són el mateix objecte, és a dir, que la referència en memòria es la mateixa.

String a = new String("Joan");
String b = new String("Joan");
assertSame(a, a);
assertNotSame(a, b);

Nota

Utilitzem new String() per forçar el compilador que cree dos objectes diferents en dos referències de memòria diferents.

Vegeu: StackOverflow – will two strings with same content be stored in the same memory location?

assertArrayEquals

La instrucció assertArrayEquals(expected, actual) s'utilitza per comprovar si dos arrays són iguals. Si no ho son, la prova fallarà.

assertArrayEquals pot ser utilitzada en arrays de diferents tipus de dades, que poden ser tipus de dades primitives o objectes.

double[] intExpected = {1, 2, 3};
double[] intActual = {1, 2, 3};
assertArrayEquals(intExpected, intActual),

double[] doubleExpected = {1.0, 2.0, 3.0};
double[] doubleActual = {1.0, 2.0, 3.0};
assertArrayEquals(doubleExpected, doubleActual ),

String[] stringExpected = {"Hello", "World!"};
String[] stringActual = {"Hello", "World!"};
assertArrayEquals(stringExpected, stringActual)

En el cas de arrays de tipus double[], es pot definir una tolerància (delta):

double[] doubleToleranceExpected = {1.0, 2.0, 3.0};
double[] doubleToleranceActual = {1.09, 2.0, 2.91};
assertArrayEquals(doubleToleranceExpected, doubleToleranceActual, 0.1),

assertAll

La instrucció assertAll(Executable... executables) s'utilitza per comprovar que tots els assert proporcionats no llancen un error del tipus AssertionError.

En altres paraules, serveix per comprovar que totes les comprovacions en un cas de prova es compleixen.

Aquesta instrucció és útil, ja que encara que un cas de prova done error, tots els següents casos de prova també s'executaran.

Cal utilitzar aquesta instrucció quan un únic cas de prova ha de realitzar més d'una comprovació.

assertFalse(1 > 0); // Aquest cas de prova fallarà, i per tant,
assertTrue(1 > 0);  // la següent intrucció no s'executarà

En canvi:

Informació

La sintaxi () -> es correspon a Lambda expressions.

assertAll(
    () -> assertFalse(1 > 0), // Aquest cas de prova fallarà, però
    () -> assertTrue(1 > 0)   // la següent intrucció també es comprovarà.
)

Exemple d'assercions

Aquest fitxer anirà a la carpeta test: src/test/java/ud5/examples,

AssertionTest.java
package ud4.examples;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

public class AssertionTest {

    @Test
    @DisplayName("assertEquals")
    void assertMultipleEquals(){
        assertAll(
            () -> assertEquals(10, 3 + 7), // Integer
            () -> assertEquals('b', 'a' + 1), // Char
            () -> assertEquals(1.5, 3.0 / 2), // Double
            () -> assertEquals(1.5, 3.1 / 2, 0.1), // Double with tolerance (delta)
            () -> assertEquals("HELLO", "hello".toUpperCase()) // String
        );
    }

    @Test
    @DisplayName("assertTrue & assertFalse")
    void assertMultipleTrueFalse(){
        assertAll(
            () -> assertTrue(-10 < 0),
            () -> assertFalse(-10 > 0)
        );
    }

    @Test
    @DisplayName("assertNull & assertNotNull")
    void assertMultipleNull(){
        String a = null;
        String b = "Joan";
        assertAll(
            () -> assertNull(a),
            () -> assertNotNull(b)
        );
    }

    @Test
    @DisplayName("assertSame & assertNotSame")
    void assertMultipleSame(){
        // https://stackoverflow.com/questions/15805578/will-two-strings-with-same-content-be-stored-in-the-same-memory-location
        String a = new String("Joan");
        String b = new String("Joan");
        assertAll(
            () -> assertSame(a, a),
            () -> assertNotSame(a, b)
        );
    }

    @Test
    @DisplayName("assertArrayEquals")
    void assertMultipleArray(){
        double[] intExpected = {1, 2, 3};
        double[] intActual = {1, 2, 3};

        double[] doubleExpected = {1.0, 2.0, 3.0};
        double[] doubleActual = {1.0, 2.0, 3.0};

        double[] doubleToleranceExpected = {1.0, 2.0, 3.0};
        double[] doubleToleranceActual = {1.09, 2.0, 2.91};

        String[] stringExpected = {"Hello", "World!"};
        String[] stringActual = {"Hello", "World!"};

        assertAll(
            () -> assertArrayEquals(intExpected, intActual),
            () -> assertArrayEquals(doubleExpected, doubleActual ),
            () -> assertArrayEquals(doubleToleranceExpected, doubleToleranceActual, 0.1),
            () -> assertArrayEquals(stringExpected, stringActual)
        );
    }
}

Anotacions

JUnit proporciona una sèrie d'anotacions per controlar com s'executen les proves i per proporcionar informació addicional sobre aquestes.

@Test

@Test és l'anotació principal de JUnit, que s'utilitza per especificar quins mètodes són proves. JUnit executarà automàticament tots els mètodes anotats amb @Test.

@DisplayName

L'anotació @DisplayName s'utilitza per especificar un nom personalitzat a una classe o una prova. Aquest nom serà mostrat en els resultats, que farà que siga més fàcil identificar la prova i saber quin és el seu propòsit.

Per defecte s'utilitza el nom de la classe o el mètode.

@DisplayName("My custom test class name")
public class MyTestClass {

    @DisplayName("My custom test name")
    @Test
    public void myTestMethod() {
        // test code here
    }

    // Additional test methods
}

@BeforeAll i @AfterAll

L'anotació @BeforeAll s'utilitza per especificar el codi que s'executarà una sola vegada abans que les proves s'executen.

L'anotació @AfterAll s'utilitza per especificar el codi que s'executarà una vegada tots les proves han acabat.

Aquests mètodes són útils per inicialitzar recursos que s'utilitzen en multiples mètodes, com una connexió a una base de dades o l'accés a un fitxer compartit.

Important

Aquests mètodes han de ser declarats com a static.

Exemple

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

public class MyTestClass {
    @BeforeAll
    public static void setUp() {
        // code that should be run once before all the test methods
        // this method should be static
    }

    @AfterAll
    public static void tearDown() {
        // code that should be run once after all the test methods
        // this method should be static
    }

    @Test
    public void test1() {
        // test code here
    }

    @Test
    public void test2() {
        // test code here
    }
}

En aquest exemple, s'executaran els mètodes en l'ordre:

  1. setUp()
  2. test1()
  3. test2()
  4. tearDown()

@BeforeEach i @AfterEach

L'anotació @BeforeEach s'utilitza per especificar el codi que s'executarà abans que cada test s'execute.

L'anotació @AfterEach s'utilitza per especificar el codi que s'executarà després que cada test s'execute.

Aquests mètodes són útils per inicialitzar recursos que en un sol test, com objectes simulats (mock objects) o un fitxer temporal.

Exemple

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

public class MyTestClass {
    @BeforeEach
    public void setUp() {
        // code that should be run before each test method
    }

    @AfterEach
    public void tearDown() {
        // code that should be run after each test method
    }

    @Test
    public void test1() {
        // test code here
    }

    @Test
    public void test2() {
        // test code here
    }
}

En aquest exemple, s'executaran els mètodes en l'ordre:

  1. setUp()
  2. test1()
  3. tearDown()
  4. setUp()
  5. test2()
  6. tearDown()

@Disabled

L'anotació @Disabled s'utilitza per deshabilitar un test temporalment. JUnit ometrà aquest test mentres estiga deshabilitat.

Aquesta anotació pot ser útil, per exemple, quan la prova falla i esteu treballant per arreglar-lo o quan la funcionalitat que s'està provant encara no està implementada. D'aquesta manera podeu mantindre la prova en el codi font sense la necessitat de comentar-lo i també es pot veure el motiu pel qual està desactivat.

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

public class MyTestClass {
    @Test
    public void test1() {
        // test code here
    }

    @Disabled("This test is currently not working, we are working on it")
    @Test
    public void test2() {
        // test code here
    }
}

@Nested

L'anotació @Nested s'utilitza per definir una classe interna dins d'una altra classe de tests. D'aquesta manera es poden agrupar tests que tinguen relació entre ells.

La classe @Nested pot utilitzar les anotacions @BeforeEach, @AfterEach, @BeforeAll i @AfterAll, igual que qualsevol altra classe i sols s'aplicaran als mètodes continguts en aquesta classe o a les seues subclasses.

import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

public class MyTestClass {
    @Test
    public void test1() {
        // test code here
    }

    @Nested
    public class NestedTestClass {
        @Test
        public void test2() {
            // test code here
        }

        @Test
        public void test3() {
            // test code here
        }
    }
}

Comentaris