JUnit Comprehensive Cheatsheet¶
Installazione¶
Tabella_139_
Core Annotations (JUnit 5)¶
TABELLA: 140_
Asserzioni di base¶
Tabella_141_
Asserzioni avanzate¶
Tabella_142_
Annotazioni di prova parametrizzate¶
TABELLA
Comandi per anziani¶
Tabella_144_
Comandi di livello¶
Tabella_145
Configurazione¶
# Maven Surefire Plugin Configurazione¶
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.2</version>
<configuration>
<!-- Include/exclude test patterns -->
<includes>
<include>**/*Test.java</include>
<include>**/*Tests.java</include>
</includes>
<excludes>
<exclude>**/*IntegrationTest.java</exclude>
</excludes>
<!-- Parallel execution -->
<parallel>methods</parallel>
<threadCount>4</threadCount>
<!-- Tag filtering -->
<groups>unit, integration</groups>
<excludedGroups>slow</excludedGroups>
<!-- System properties -->
<systemPropertyVariables>
<env>test</env>
</systemPropertyVariables>
</configuration>
</plugin>
Configurazione del test di grado¶
test {
useJUnitPlatform {
// Include/exclude tags
includeTags 'unit', 'integration'
excludeTags 'slow'
// Include/exclude engines
includeEngines 'junit-jupiter'
excludeEngines 'junit-vintage'
}
// Parallel execution
maxParallelForks = 4
// Test filtering
filter {
includeTestsMatching '*Test'
excludeTestsMatching '*IntegrationTest'
}
// System properties
systemProperty 'env', 'test'
// Test logging
testLogging {
events "passed", "skipped", "failed"
exceptionFormat "full"
showStandardStreams = true
}
// Fail fast
failFast = true
}
JUnit Platform Properties (junit-platform.properties)¶
# Parallel execution
junit.jupiter.execution.parallel.enabled = true
junit.jupiter.execution.parallel.mode.default = concurrent
junit.jupiter.execution.parallel.config.strategy = fixed
junit.jupiter.execution.parallel.config.fixed.parallelism = 4
# Test instance lifecycle
junit.jupiter.testinstance.lifecycle.default = per_class
# Display name generation
junit.jupiter.displayname.generator.default = org.junit.jupiter.api.DisplayNameGenerator$ReplaceUnderscores
# Automatic extension detection
junit.jupiter.extensions.autodetection.enabled = true
# Deactivate conditions
junit.jupiter.conditions.deactivate = org.junit.*DisabledCondition
Common Use Cases¶
Use Case: Basic Unit Test with Setup and Teardown¶
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
class UserServiceTest {
private UserService userService;
private Database mockDb;
@BeforeEach
void setUp() {
mockDb = new MockDatabase();
userService = new UserService(mockDb);
}
@Test
@DisplayName("Should create user with valid data")
void testCreateUser() {
User user = new User("john@example.com", "password123");
User created = userService.createUser(user);
assertNotNull(created.getId());
assertEquals("john@example.com", created.getEmail());
}
@AfterEach
void tearDown() {
mockDb.close();
}
}
Use Case: Parameterized Testing for Multiple Inputs¶
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.*;
class CalculatorTest {
@ParameterizedTest
@CsvSource({
"1, 1, 2",
"2, 3, 5",
"10, -5, 5",
"0, 0, 0"
})
void testAddition(int a, int b, int expected) {
Calculator calc = new Calculator();
assertEquals(expected, calc.add(a, b));
}
@ParameterizedTest
@ValueSource(strings = {"", " ", "\t", "\n"})
void testBlankStrings(String input) {
assertTrue(StringUtils.isBlank(input));
}
@ParameterizedTest
@MethodSource("provideEmailTestCases")
void testEmailValidation(String email, boolean expected) {
assertEquals(expected, EmailValidator.isValid(email));
}
static Stream<Arguments> provideEmailTestCases() {
return Stream.of(
Arguments.of("test@example.com", true),
Arguments.of("invalid.email", false),
Arguments.of("@example.com", false)
);
}
}
Use Case: Eccezione Testing and Timeout Verification¶
import org.junit.jupiter.api.Test;
import java.time.Duration;
import static org.junit.jupiter.api.Assertions.*;
class PaymentServiceTest {
@Test
void shouldThrowExceptionForInvalidAmount() {
PaymentService service = new PaymentService();
Exception exception = assertThrows(
IllegalArgumentException.class,
() -> service.processPayment(-100)
);
assertEquals("Amount must be positive", exception.getMessage());
}
@Test
void shouldCompleteWithinTimeout() {
PaymentService service = new PaymentService();
assertTimeout(Duration.ofSeconds(2), () -> {
service.processPayment(100);
});
}
@Test
void shouldNotThrowExceptionForValidPayment() {
PaymentService service = new PaymentService();
assertDoesNotThrow(() -> {
service.processPayment(100);
});
}
}
Use Case: Nested Tests for Organized Test Structure¶
import org.junit.jupiter.api.*;
@DisplayName("Shopping Cart Tests")
class ShoppingCartTest {
@Nested
@DisplayName("When cart is empty")
class EmptyCart {
private ShoppingCart cart;
@BeforeEach
void setUp() {
cart = new ShoppingCart();
}
@Test
@DisplayName("Should have zero items")
void testItemCount() {
assertEquals(0, cart.getItemCount());
}
@Test
@DisplayName("Should have zero total")
void testTotal() {
assertEquals(0.0, cart.getTotal());
}
}
@Nested
@DisplayName("When cart has items")
class CartWithItems {
private ShoppingCart cart;
@BeforeEach
void setUp() {
cart = new ShoppingCart();
cart.addItem(new Item("Book", 29.99));
cart.addItem(new Item("Pen", 2.50));
}
@Test
@DisplayName("Should calculate correct total")
void testTotal() {
assertEquals(32.49, cart.getTotal(), 0.01);
}
@Test
@DisplayName("Should allow item removal")
void testRemoveItem() {
cart.removeItem(0);
assertEquals(1, cart.getItemCount());
}
}
}
Use Case: Integration Testing with Test Lifecycle Management¶
import org.junit.jupiter.api.*;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class DatabaseIntegrationTest {
private static DatabaseConnection connection;
private UserRepository userRepository;
@BeforeAll
void setUpDatabase() {
// Expensive one-time setup
connection = DatabaseConnection.connect("jdbc:h2:mem:test");
connection.runMigrations();
}
@BeforeEach
void setUp() {
userRepository = new UserRepository(connection);
connection.beginTransaction();
}
@Test
@Tag("integration")
void testUserPersistence() {
User user = new User("test@example.com");
userRepository.save(user);
User retrieved = userRepository.findById(user.getId());
assertEquals(user.getEmail(), retrieved.getEmail());
}
@AfterEach
void tearDown() {
connection.rollbackTransaction();
}
@AfterAll
void tearDownDatabase() {
connection.close();
}
}
Migliori Pratiche¶
-
Utilizzare i nomi dei test descrittivi: Leverage
@DisplayNameper rendere chiari gli obiettivi di prova. I nomi dei test dovrebbero descrivere ciò che viene testato e il risultato atteso (ad esempio, "Dovreste gettare l'eccezione quando la quantità è negativa"). -
Follow AAA pattern: Test struttura con Arrange (setup), Act (execute), e Assert (verificare) sezioni per chiarezza e manutenbilità.
Una cosa per prova? Ogni metodo di prova deve verificare un singolo comportamento o uno scenario. Questo rende i guasti più facili da diagnosticare e i test più facili da mantenere.
-
Usi @BeforeEach e @AfterEach saggiamente**: Inizializzare i dispositivi di prova comuni in
@BeforeEachma evitare l'installazione eccessiva che rende i test difficili da capire. Pulire le risorse in@AfterEach_. -
Preferire @Parameterized Test per più casi simili: Invece di scrivere più metodi di test simili, utilizzare test parametrizzati per testare la stessa logica con diversi input.
-
Utilizzare tutte le affermazioni relative: Asserzioni relative al gruppo con
assertAll()per vedere tutti i guasti in una sola volta piuttosto che fermarsi al primo fallimento. -
Tag test appropriatamente: Usa
@Tagper classificare i test (unità, integrazione, lenta) in modo da poter eseguire selettivamente suite di test in diversi ambienti o fasi CI/CD. -
** Interdipendenze di test avoidi ** I test dovrebbero essere indipendenti e in grado di eseguire in qualsiasi ordine. Mai contare su ordine di esecuzione o stato da altri test.
-
Utilizzare messaggi di affermazione significativi: Fornire messaggi di errore personalizzati alle affermazioni per rendere più facile il debug: - Ok.
-
I test sono veloci. I test unitari dovrebbero essere eseguiti rapidamente. Spostare i test lenti (database, rete) per separare le suite di test di integrazione utilizzando tag o set sorgente separati.
Risoluzione dei problemi¶
Traduzione: I test di culla sempre UP-TO-DATE | Usa gradle cleanTest test o gradle test --rerun-tasks_ per forzare l'esecuzione del test. Controllare se i file sorgente di prova sono nella directory corretta (src/test/java). #