Tutorial JUnit5

Tutorial su JUnit5

In questo tutorial su JUnit, introdurremo i concetti di base di JUnit5 e le sue nuove funzionalità utilizzando degli esempi. Nel mondo Java, JUnit è uno dei framework più popolari utilizzati per implementare test unitari sul codice Java. JUnit aiuta principalmente gli sviluppatori a testare il proprio codice sulla JVM autonomamente.

Architettura di JUnit5

JUnit Platform

  • Lancia framework di testing sulla JVM
  • Dispone di un’API TestEngine utilizzata per costruire un framework di testing che funziona sulla piattaforma JUnit

JUnit Jupiter

  • Unione di un nuovo modello di programmazione per scrivere test e di un modello di estensione per le estensioni
  • Aggiunta di nuove annotazioni come @BeforeEach, @AfterEach, @AfterAll, @BeforeAll, ecc.

JUnit Vintage

  • Fornisce supporto per eseguire test delle versioni precedenti di JUnit 3 e 4 su questa nuova piattaforma

JUnit Maven Dependencies

Per implementare casi di test basati su JUnit5 in un progetto, aggiungi la seguente dipendenza al file pom.xml del progetto:

  • Libreria JUnit 5
<dependency>
     <groupId>org.junit.jupiter</groupId>
     <artifactId>junit-jupiter-engine</artifactId>
     <version>5.1.1</version>
     <scope>test</scope>
</dependency>
<dependency>
     <groupId>org.junit.platform</groupId>
     <artifactId>junit-platform-runner</artifactId>
     <version> 1.1.1</version>
     <scope>test</scope>
</dependency>
  • Fornitore Maven Surefire JUnit5 per eseguire i test unitari dove l’IDE non supporta JUnit5 (se l’IDE ha il supporto, questo punto non è richiesto)
<plugin>
     <artifactId>maven-surefire-plugin</artifactId>
     <version>2.19.1</version>
     <dependencies>
          <dependency>
               <groupId>org.junit.platform</groupId>
               <artifactId>junit-platform-surefire-provider</artifactId>
               <version>1.0.2</version>
          </dependency>
     </dependencies>
</plugin>

JUnit5 Nuove Funzionalità

Richiede Java 8 o versioni successive in fase di esecuzione. Tuttavia, è ancora possibile testare codice compilato con versioni precedenti di Java. Sono state introdotte diverse nuove funzionalità.

Annotation di JUnit

Ecco di seguito alcune annotazioni comunemente utilizzate fornite in esso:

Annotation Description
@Test Denotes a test method
@DisplayName Declares a custom display name for the test class or test method
@BeforeEach Denotes that the annotated method should be executed before each test method
@AfterEach Denotes that the annotated method should be executed after each test method
@BeforeAll Denotes that the annotated method should be executed before all test methods
@AfterAll Denotes that the annotated method should be executed after all test methods
@Disable Used to disable a test class or test method
@Nested Denotes that the annotated class is a nested, non-static test class
@Tag Declare tags for filtering tests
@ExtendWith Register custom extensions
package com.journaldev;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class JUnit5Sample1Test {

  @BeforeAll
  static void beforeAll() {
    System.out.println("**--- Executed once before all test methods in this class ---**");
  }

  @BeforeEach
  void beforeEach() {
    System.out.println("**--- Executed before each test method in this class ---**");
  }

  @Test
  void testMethod1() {
    System.out.println("**--- Test method1 executed ---**");
  }

  @DisplayName("Test method2 with condition")
  @Test
  void testMethod2() {
    System.out.println("**--- Test method2 executed ---**");
  }

  @Test
  @Disabled("implementation pending")
  void testMethod3() {
	  System.out.println("**--- Test method3 executed ---**");
  }

  @AfterEach
  void afterEach() {
    System.out.println("**--- Executed after each test method in this class ---**");
  }

  @AfterAll
  static void afterAll() {
    System.out.println("**--- Executed once after all test methods in this class ---**");
  }


}

Possiamo eseguire la classe di test JUnit sopra menzionata in Eclipse -> Esegui come -> Test JUnit.

Asserzioni di JUnit

Ogni metodo di test deve essere valutato rispetto a una condizione vera utilizzando asserzioni in modo che il test possa continuare a eseguirsi. Le asserzioni di JUnit Jupiter sono mantenute nella classe org.junit.jupiter.api.Assertions. Tutti i metodi sono statici.

Assertion Description
assertEquals(expected, actual) Fails when expected does not equal actual
assertFalse(expression) Fails when expression is not false
assertNull(actual) Fails when actual is not null
assertNotNull(actual) Fails when actual is null
assertAll() Group many assertions and every assertion is executed even if one or more of them fails
assertTrue(expression) Fails if expression is not true
assertThrows() Class to be tested is expected to throw an exception
@Test
void testAssertEqual() {
	 assertEquals("ABC", "ABC");
	 assertEquals(20, 20, "optional assertion message");
	 assertEquals(2 + 2, 4);
}

@Test
void testAssertFalse() {
	 assertFalse("FirstName".length() == 10);
	 assertFalse(10 > 20, "assertion message");
}

@Test
void testAssertNull() {
     String str1 = null;
	 String str2 = "abc";
	 assertNull(str1);
	 assertNotNull(str2);	
}

@Test
void testAssertAll() {
	 String str1 = "abc";
	 String str2 = "pqr";
	 String str3 = "xyz";
	 assertAll("numbers",
	      () -> assertEquals(str1,"abc"),
		  () -> assertEquals(str2,"pqr"),
		  () -> assertEquals(str3,"xyz")
	 );
	 //decommenta il codice sottostante e comprendi l'esecuzione di ogni asserzione
     /*assertAll("numbers",
		  () -> assertEquals(str1,"abc"),
		  () -> assertEquals(str2,"pqr1"),
		  () -> assertEquals(str3,"xyz1")
	 );*/
}

@Test
void testAssertTrue() {
	 assertTrue("FirstName".startsWith("F"));
	 assertTrue(10  {
	      throw new IllegalArgumentException("Illegal Argument Exception occured");
	 });
	 assertEquals("Illegal Argument Exception occured", exception.getMessage());
}

Importazioni di JUnit5

Le sue classi di test necessitano dell’importazione dello statement org.junit.jupiter.api.Test e non org.junit.Test. Inoltre, i metodi di test non devono essere pubblici e appartenenti al package locale.

import org.junit.jupiter.api.Test;

Assunzioni JUnit5

Le assunzioni sono metodi statici nella classe org.junit.jupiter.api.Assumptions. Eseguiranno un test solo quando la condizione specificata viene soddisfatta, altrimenti il test verrà interrotto. Il test interrotto non causerà il fallimento della build. Quando un’assunzione fallisce, viene lanciata un’eccezione org.opentest4j.TestAbortedException e il test viene saltato.

Assumptions Description
assumeTrue Execute the body of lamda when the positive condition hold else test will be skipped
assumeFalse Execute the body of lamda when the negative condition hold else test will be skipped
assumingThat Portion of the test method will execute if an assumption holds true and everything after the lambda will execute irrespective of the assumption in assumingThat() holds
@Test
void testAssumeTrue() {
     boolean b = 'A' == 'A';
     assumeTrue(b);
     assertEquals("Hello", "Hello");
}

@Test
@DisplayName("test executes only on Saturday")
public void testAssumeTrueSaturday() {
     LocalDateTime dt = LocalDateTime.now();
     assumeTrue(dt.getDayOfWeek().getValue() == 6);
     System.out.println("further code will execute only if above assumption holds true");
}

@Test
void testAssumeFalse() {
     boolean b = 'A' != 'A';
     assumeFalse(b);
     assertEquals("Hello", "Hello");
}

@Test
void testAssumeFalseEnvProp() {
     System.setProperty("env", "prod");
     assumeFalse("dev".equals(System.getProperty("env")));
     System.out.println("further code will execute only if above assumption hold");
}

@Test
void testAssumingThat() {
     System.setProperty("env", "test");
     assumingThat("test".equals(System.getProperty("env")),
          () -> {
               assertEquals(10, 10);
               System.out.println("perform below assertions only on the test env");
               });

     assertEquals(20, 20);
     System.out.println("perform below assertions on all env");
}

Classi di test nidificate JUnit

I test nidificati consentono di creare classi nidificate ed eseguire tutti i suoi metodi di test. Le classi interne devono essere non statiche. Basta annotare le classi interne con @Nested e tutti i metodi di test al suo interno verranno eseguiti.

@BeforeAll
static void beforeAll() {
     System.out.println("**--- JUnit5Sample4Test :: beforeAll :: Executed once before all test methods ---**");
}
 
@BeforeEach
void beforeEach() {
	 System.out.println("**--- JUnit5Sample4Test :: beforeEach :: Executed before each test method ---**");
}

@AfterEach
void afterEach() {
	 System.out.println("**--- JUnit5Sample4Test :: afterEach :: Executed after each test method ---**");
}

@AfterAll
static void afterAll() {
	 System.out.println("**--- JUnit5Sample4Test :: afterAll :: Executed after all test method ---**");
}
 
     @Nested
     class InnerClass {
 
          @BeforeEach
          void beforeEach() {
               System.out.println("**--- InnerClass :: beforeEach :: Executed before each test method ---**");
          }
 
          @AfterEach
          void afterEach() {
        	   System.out.println("**--- InnerClass :: afterEach :: Executed after each test method ---**");
          }
 
          @Test
          void testMethod1() {
        	   System.out.println("**--- InnerClass :: testMethod1 :: Executed test method1 ---**");
          }
 
          @Nested
          class InnerMostClass {
 
               @BeforeEach
               void beforeEach() {
                    System.out.println("**--- InnerMostClass :: beforeEach :: Executed before each test method ---**");
               }
 
               @AfterEach
               void afterEach() {
            	    System.out.println("**--- InnerMostClass :: afterEach :: Executed after each test method ---**");
               }
 
               @Test
               void testMethod2() {
            	    System.out.println("**--- InnerMostClass :: testMethod2 :: Executed test method2 ---**");
               }
        }
    }

Eccezione del test JUnit

Vi sono situazioni in cui ci si aspetta che i metodi lancino un’eccezione in una specifica condizione. assertThrows farà fallire il test se il metodo specificato non solleva l’eccezione specificata.

Throwable exception = assertThrows(IllegalArgumentException.class, () -> {
     throw new IllegalArgumentException("Illegal Argument Exception occured");
});
assertEquals("Illegal Argument Exception occured", exception.getMessage());

Esecuzione dei test di JUnit

I test di unità possono essere eseguiti in molti modi, due dei modi sono i seguenti:

  • Utilizzare l’IDE Eclipse Oxygen.3a (4.7.3a) e aprire il file di test da eseguire. Fare clic con il pulsante destro del mouse sul file e scegliere l’opzione Esegui come seguita da Test JUnit
  • Utilizzare il comando mvn test nella finestra di comando di Windows

Riepilogo

Abbiamo esplorato JUnit5 e le sue nuove funzionalità con alcuni esempi. Abbiamo anche visto come possiamo utilizzare le annotazioni, le asserzioni, le presunzioni, le eccezioni di JUnit e scrivere classi di test nidificate.

È possibile scaricare il progetto di esempio completo dal nostro Repository GitHub.

Source:
https://www.digitalocean.com/community/tutorials/junit5-tutorial