<div class="page">
<div class="cover text-center">
<img class="mx-auto" src=/itb/images/logo_mislata.png alt="logo">
# Tests unitaris amb JUnit
<div class="text-end fit-content ms-auto my-3 mt-auto pt-3">
<p><strong>Autor:</strong> Joan Puigcerver Ibáñez</p>
<p><strong>Correu electrònic:</strong> j.puigcerveribanez@edu.gva.es</p>
<p><strong>Curs:</strong> 2024/2025</p>
</div>
<div>
<p class="fw-bold mb-0">Llicència: BY-NC-SA</p>
<p class="d-none d-md-block">(Reconeixement - No Comercial - Compartir Igual)</p>
<a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.ca" target="_blank">
<img class="mx-auto" src="/itb/images/license.png" alt="Licence"/>
</a>
</div><!--license-->
</div><!--cover-->
</div><!--page-->
{:toc}
## Introducció
El __test unitari__ és una tècnica de verificació de programari que s'utilitza
per assegurar que cada part ("unitat") del programa funciona correctament.
Aquesta tècnica es basa en la idea de dividir el programa en parts més petites i
escriure tests per a cada part per comprovar que totes elles funcionen correctament.
Això és útil perquè, quan es produeixen canvis en un programa, és més fàcil determinar què ha anat malament
si es poden comprovar les parts individuals del programa. Això també pot ajudar a detectar errors més ràpidament
i a prevenir la introducció de nous errors durant el manteniment del programa.
## Tests unitaris en Java
Java té diverses eines per escriure i executar tests unitaris.
Una de les més comunes és __JUnit__, que és una llibreria de Java que proporciona
un marc per escriure i executar tests unitaris.
Per utilitzar JUnit, primer hauràs de descarregar-lo i afegir-lo al teu projecte Java.
### Instal·lar JUnit mitjançant Maven
- __Documentació IntelliJ__: https://www.jetbrains.com/help/idea/junit.html
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 a gran escala i ho fa molt més reproducible,
ja que es pot utilitzar Maven per crear fàcilment una estructura de directoris comuna
i automatitzar tasques com la compilació, testeig i empaquetat.
Quan creem un projecte en IntelliJ, ens pregunta si volem utilitzar Maven.
En el nostre cas, ja vam crear el projecte com un projecte Maven.
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__.

3. A la finestra, escriu `org.junit.jupiter:junit-jupiter` a la barra de búsqueda.
Sel·lecciona la dependència necessària i prem __Add__ per afegir-la.

Aquesta acció harià afegit al teu __pom.xml__ un text semblant a:
```xml
<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 realitzars a l'script 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.

Després de actualitzar, hauria de quedar aixina:

### Crear tests
Anem a crear tests per comprovar que el programa [DebugRockPaperScissors](../../UD1/exercicis/03_debugging.html#exercici-2%3A-debugrockpaperscissors)
funciona correctament.
Per poder realitzar els tests, s'ha creat una nova versió del programa que incorpora el mètode `int guanya(String jugador1, String jugador2)`,
que determina quin jugador ha guanyat la partida:
```java
package ud5.examples;
import java.util.Locale;
import java.util.Scanner;
public class DebugRockPaperScissors {
/**
* Determina quin jugador ha guanyat la partida de pedra paper i tisores.
* <p>
* Aquest mètode retorna 0 si hi ha hagut un empat, 1 si el jugador1 ha guanyat la partida,
* i 2 si el jugador2 ha guanyat.
*
* @param jugador1 Jugada del jugador1 (pedra, paper o tisores)
* @param jugador2 Jugada del jugador1 (pedra, paper o tisores)
* @return 0 si hi ha un empat; 1 si guanya jugador1; 2 si guanya jugador 2.
*/
public static int guanya(String jugador1, String jugador2){
if(jugador1.equals(jugador2))
return 0;
else if (jugador1.equals("paper")) {
return jugador2.equals("pedra") ? 1 : 2;
} else if (jugador1.equals("pedra")) {
return jugador2.equals("paper") ? 1 : 2;
} else {
return jugador2.equals("paper") ? 1 : 2;
}
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in).useLocale(Locale.US);
System.out.print("Introdueix l'elecció del jugador 2 (pedra/paper/tisores): ");
String p1 = in.nextLine();
System.out.print("Introdueix l'elecció del jugador 2 (pedra/paper/tisores): ");
String p2 = in.nextLine();
int guanyador = guanya(p1, p2);
if (guanyador == 0)
System.out.println("Empat");
else
System.out.printf("Guanya el jugador %d\n", guanyador);
}
}
```
Anem a comprovar si el mètode està ben implementat utilitzant tests unitaris:
1. Situat sobre el nom de la classe `DebugRockPaperScissors` i prem `Alt+Insert`.
Selecciona __Test__.
També pots prémer `Alt+Enter` o clicar el botó dret i selecciona __Show Context Actions__.
Des del menú, selecciona __Create Test__.

2. Selecciona els mètodes que vols provar en el diàleg i prem __OK__.

3. L'acció anterior ha creat un nou fixer en `src/test/java`, la carpeta dessignada on guardar els tests
del projecte. El nou fitxer es troba en la mateixa estructura de paquests que el codi font: `ud5.examples`.

4. Anem a crear un test per comprovar si mètode determina correctament si la pedra guanya a les tisores.
Per fer-ho utilitzarem, `assertEquals()` que comprovarà que els parametres d'entrada i eixida son iguals.
Podem utilitzar `@DisplayName` per donar-li un nom més descriptiu al test:
```java
@Test
@DisplayName("Guanya: Pedra vs Tisores")
void pedraGuanyaTisores(){
assertEquals(1, DebugRockPaperScissors.guanya("pedra", "tisores"));
}
```
5. Comprovem també si hi ha un empat quan els dos jugadors elegeixen 'tisores'.
```java
@Test
@DisplayName("Empat: Tisores vs Tisores")
void tisoresEmpataTisores(){
assertEquals(0, DebugRockPaperScissors.guanya("tisores", "tisores"));
}
```
### Llançar els tests
Una vegada s'han establit els tests, anem a executar-los per veure si el mètode `int guanya(String jugador1, String jugador2)` funciona
correctanent.
Per executar-los, s'ha de clicar el botó {.icon} __Run__.

Una vegada executats, podem consultar els resultats:

Es poden observals els resultats de cada test per separat.
En aquest cas tenim:
- El test __Empat: Tisores vs Tisores__ s'ha executant correctament.
- El test __Guanya: Pedra vs Tisores__ ha fallat. S'esperava (__expected__)
que guanyara el jugador 1 i s'ha obtingut (__actual__) que ha guanyat el
jugador 2.
## Assert
En JUnit, la instrucció __assert__ és una instrucció que s'utilitza per comprovar que una
determinada condició és certa durant l'execució d'un test unitari.
Si la condició no es compleix, el test 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 testos unitaris.
### assertEquals
La instrucció `assertEquals` s'utilitza per comprovar que dos valor són iguals,
on __expected__ és el valor que esperaves i __actual__ és el valor que has obtingut.
Si "expected" i "actual" són iguals, el test continuarà executant-se.
Si no ho són, el test fallarà amb una `AssertionError`.
Aquest mètode utilitza el mètode `equals` dels objectes
per determinar si els objetes són iguals o no.
```java
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`):
```java
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, el test fallarà.
```java
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 demanat, el test fallarà.
```java
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ó, el test fallarà.
Aquest mètode utilitza l'operador `==`,
que en els objectes mira si exactament són el mateix objecte,
és a dir, que la referència en mèmoria es la mateixa.
```java
String a = new String("Joan");
String b = new String("Joan");
assertSame(a, a);
assertNotSame(a, b);
```
::: note
Utilitzem new String per forçar el compilador que cree dos objectes diferents en dos referències de memòria diferents.
Vegeu: https://stackoverflow.com/questions/15805578/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, el test fallarà.
`assertArrayEquals` pot ser utilitzada en arrays de diferents tipus de dades, que poden
ser tipus de dades __primitives__ o objectes.
```java
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`):
```java
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 dóne error,
tots els següents casos de prova també s'executaran.
```java
assertFalse(1 > 0); // Aquest cas de prova fallarà, i per tant,
assertTrue(1 > 0); // la següent intrucció no s'executarà
```
En canvi:
```java
assertAll(
() -> assertFalse(1 > 0), // Aquest cas de prova fallarà, però
() -> assertTrue(1 > 0) // la següent intrucció també es comprovarà.
)
```
Aquesta instrucció és útil quan un únic cas de prova ha de realitzar més d'una comprovació.
```java
@Test
@DisplayName("Multiply possitive numbers")
void multiplyPositiveNumbers(){
assertAll(
() -> assertEquals(1, multiply(1, 1)),
() -> assertEquals(2, multiply(1, 2)),
() -> assertEquals(4, multiply(2, 2)),
() -> assertEquals(6, multiply(2, 3)),
() -> assertEquals(100, multiply(10, 10))
);
}
```
La sintaxi `() ->` es correspon a [Lambda expressions](https://www.w3schools.com/java/java_lambda.asp).
## Example assertions
Aquest fitxer anirà a la carpeta test: __src/test/java/ud5/examples__,
- <a href="/itb/DAM-ED/files/ud5/examples/AssertionTest.java" download="AssertionTest.java">AssertionTest.java</a>
```java
package ud5.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)
);
}
}
```