Link Cerca Menu Expand Document

Eines Java

junit

JUnit permet automatitzar les proves de codi en Java.

Una classe de proves en Java permet l’execució de proves unitàries sobre el codi que volem validar. A JUnit (5) podem indicar que una classe és de proves utilitzant anotacions davant dels mètodes d’aquesta classe. Principalment:

  • @BeforeAll: mètode estàtic que s’executarà abans de totes les proves.
  • @BeforeEach: mètode que s’executarà abans de cada prova.
  • @Test: anotació més important, indica que el mètode és una prova a executar.
  • @AfterEach: mètode que s’executarà després de cada prova.
  • @AfterAll: mètode que s’executarà després de totes les proves.

Una prova (test) té un codi que pot fallar. Si no falla, la prova és correcta. Pot fallar per dues raons:

  • Que hi hagi una excepció durant l’execució de la prova que no es gestioni al codi. Es comptabilitzen com a error.
  • Que s’utilitzi una asseveració (assertion) de la llibreria JUnit, i que aquesta no es compleixi. Es comptabilitzen com a fallada (failure).

Les asseveracions de JUnit són crides a mètodes estàtics que poden resoldre’s amb aquest import (JUnit 5):

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

Aquestes són algunes de les asseveracions més importants:

  • assertTrue(boolean): falla si el paràmetre és false
  • assertFalse(boolean): falla si el paràmetre és true
  • assertEquals(object1, object2): falla si els paràmetres no són iguals
  • assertNotEquals(object1, object2): falla si els paràmetres són iguals
  • fail(): falla sempre

Totes aquestes asseveracions permeten afegir un text explicatiu per ajudar-nos a entendre el problema que s’ha produït.

Els IDE moderns detecten quan una classe és de prova gràcies a que conté anotacions de @Test, i llavors permeten executar aquestes proves visualment, fent el recompte d’errors i permetent identificar en quina part del codi s’han produït.

A continuació es poden veure dues captures d’un error i una fallada a Eclipse.

Error

Hi ha una excepció a la prova testAddCredit(). La Failure Trace mostra quina excepció ha passat (primera línia) i la línia del codi on ha passat (ShopClientImpl.java).

JUnit error

Fallada

Hi ha asseveració que falla a la prova testJustCredit(). La Failure Trace mostra el missatge fallit de l’asseveració (primera línia), la línia del test on ha passat (ShopCartClientTest.java).

JUnit failure

maven

Maven és una eina que permet gestionar la construcció i desplegament automàtics de projectes Java. Mitjançant un arxiu pom.xml, es descriu el procés de construcció i quines són les dependències d’altres projectes o llibreries. Aquestes dependències són automàticament descarregades d’un repositori central per ser utilitzades localment.

Maven té tres lifecycles predefinits:

  • default: gestiona el desplegament
  • clean: gestiona la neteja
  • site: gestiona la documentació

Cada lifecycle conté una sèrie de fases consecutives. En particular, el default conté principalment:

  • validate: valida la informació del projecte
  • compile: compila el codi font
  • test: proves unitàries del codi font
  • package: empaqueta el codi font per a ser distribuït, habitualment JAR
  • verify: proves d’integració
  • install: instal.lació del paquet al repositori local (dependències locals)
  • deploy: copiar el paquet a un repositori remot

L’executable de maven es diu mvn. Una línia de comanda típica seria:

$ mvn clean install

que executa les fases clean (lifecycle clean) i després install (lifecycle default). La fase install executa cadascuna de les fases anteriors del mateix lifecycle: validate, compile, test, package, verify i install. Aquestes fases tenen els seus goals associats en funció del packaging escollit.

Cada fase està feta d’una sèrie d’objectius o goals, que són tasques a executar. Per exemple, la fase compile està associada al plugin compiler i el goal compile. Hi ha goals que no estan associats a cap fase. Per exemple, hi ha el plugin exec-maven-plugin, que permet executar una classe Java. El plugin es diu exec, i el goal seria java. Per dir-li quina classe executar, cal indicar-ho amb la propietat exec.mainClass:

$ mvn exec:java -Dexec.mainClass="com.example.Main"

Maven utilitza una estructura de directoris predefinida:

/my-app                 
    /src/main           
        /java           => codi
        /resources      => recursos
    /src/test           
        /java           => proves
        /resources      => recursos de les proves
    /target             => arxius generats
    /pom.xml            => configuració

Aquest és el format típic d’un pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>poo_psp</groupId>
    <artifactId>common</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>common</name>
    <url>https://jgregor5.gitlab.io</url>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>
    <dependencies>    
        <dependency>
            <groupId>org.json</groupId>
            <artifactId>json</artifactId>
            <version>20210307</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.25</version>
        </dependency>
        <dependency>
            <groupId>javax.json</groupId>
            <artifactId>javax.json-api</artifactId>
            <version>1.1.4</version>
        </dependency>
        <!-- junit 5 -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.7.2</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.7.2</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.7.2</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <!-- afegir referències a plugins -->
        </plugins>
    </build>
</project>

Explicació d’algunes seccions:

  • groupId + artifactId identifiquen el paquet.
  • La versió és 1.0-SNAPSHOT, SNAPSHOT indica que el codi no és estable (darrera versió).
  • El packaging és JAR. Podria ser també WAR o EAR, per altres tipus d’entorns.
  • Les properties permeten indicar la codificació dels arxius font (UTF-8) i la versió de java (11).
  • La secció dependencies permet definir quines llibreries necessitarem perquè maven les descarregui i les utilitzi en els lifecycles. Indica sempre el groupId + artifactId (clau) i la versió.
  • La secció build permet fer referència a plugins addicionals. Poden configurar-se amb la corresponent subsecció configuration (parameter), o bé a la secció general properties (user property).

Els paquets poden buscar-se a https://mvnrepository.com/, on podem trobar la sintaxi per a afegir-los a les dependencies. Aquí també podem veure les dependències de cada paquet, si les té, que també seran descarregades.

jps i jstack

Els podeu trobar a la vostra instal.lació del JDK, a la carpeta BIN on hi ha el java i el javac. JPS permet fer una llista dels processos java que s’estan executant:

$ jps
1648 org.eclipse.equinox.launcher_1.5.800.v20200727-1323.jar
6240 Main
5608 Jps
8636 DeadlockTest

Amb JSTACK podeu veure el contingut de les piles d’execució dels fils. Imagineu que DeadlockTest té dos fils, un que es diu loop i l’altre wait (s’han suprimit els fils que no interessen):

$ jps 8636
…
"wait" #12 prio=5 os_prio=0 tid=0x000002585443e000 nid=0x9d8 waiting for monitor entry [0x000000f255aff000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at m09uf2.test.DeadlockTest.lambda$3(DeadlockTest.java:85)
        - waiting to lock <0x000000076b610938> (a m09uf2.test.DeadlockTest$MLong)
        at m09uf2.test.DeadlockTest$$Lambda$2/245257410.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

"loop" #11 prio=5 os_prio=0 tid=0x000002585443d000 nid=0x29f8 waiting on condition [0x000000f2559fe000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at m09uf2.test.DeadlockTest.lambda$2(DeadlockTest.java:71)
        - locked <0x000000076b610938> (a m09uf2.test.DeadlockTest$MLong)
        at m09uf2.test.DeadlockTest$$Lambda$1/1044036744.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)
…

Es pot veure l’estat i la línia on s’estan executant: BLOCKED (:85) i TIMED_WAITING (:71).

visualvm

El podeu instal.lar des de: https://visualvm.github.io/

Si l’executeu, i fent doble clic sobre el procés que voleu mirar, veureu:

Podeu veure diferents pestanyes:

  • Monitor: mostra CPU, espai de memòria utilitzat (heap/metaspace), classes i fils.
  • Threads: visualització gràfica dels fils que s’executen i l’estat en colors.
  • Sampler: rendiment CPU i memòria de l’aplicació.