Java/05core12 min

Testy JUnit 5 + Mockito

ProfessNet testuje kod Java w JUnit 5 (Jupiter), z Mockito do mockowania i AssertJ do czytelnych asercji. Ta lekcja pokazuje nasze wzorce — od czystej jednostki po test warstwy webowej Spring Boot.

Anatomia testu JUnit 5

Układ AAA: Arrange, Act, Assert.

class ForestNameTest {

  @Test
  void normalize_trimsAndLowercases() {
    // Arrange
    String raw = "  CORP.LOCAL ";

    // Act
    String result = ForestName.normalize(raw);

    // Assert
    assertThat(result).isEqualTo("corp.local");
  }
}

Standard ProfessNet: nazwa metody testowej opisuje zachowanie (metoda_warunek_wynik). Klasy testowe i metody mają domyślny dostęp (package-private) — w JUnit 5 nie muszą być public.

Testy parametryzowane

@ParameterizedTest
@CsvSource({"CORP, corp", "' Dmz ', dmz", "'', ''"})
void normalize(String input, String expected) {
  assertThat(ForestName.normalize(input)).isEqualTo(expected);
}

AssertJ — płynne asercje

assertThat(probes)
    .hasSize(2)
    .extracting(Probe::host)
    .containsExactly("dc01", "dc02");

assertThatThrownBy(() -> service.runScan(null))
    .isInstanceOf(IllegalArgumentException.class)
    .hasMessageContaining("forest");

Standard ProfessNet: asercje piszemy w AssertJ (assertThat) — czytelniejsze i z lepszym komunikatem błędu niż surowe assertEquals.

Mockito — izolacja zależności

@ExtendWith(MockitoExtension.class)
class InventoryServiceTest {

  @Mock private ProbeRepository repository;
  @InjectMocks private InventoryService service;

  @Test
  void runScan_returnsResultWithHostCount() {
    // Arrange
    when(repository.getHosts("corp")).thenReturn(List.of("dc01"));

    // Act
    ScanResult result = service.runScan("corp");

    // Assert
    assertThat(result.hostCount()).isEqualTo(1);
    verify(repository).getHosts("corp");
  }
}

@Mock tworzy atrapę, @InjectMocks wstrzykuje ją do testowanej klasy.

Testy warstwy webowej Spring Boot

@WebMvcTest ładuje tylko warstwę kontrolera (szybko), a zależności mockuje.

@WebMvcTest(InventoryController.class)
class InventoryControllerTest {

  @Autowired private MockMvc mockMvc;
  @MockBean private InventoryService service;

  @Test
  void scan_returns202() throws Exception {
    when(service.runScan("corp"))
        .thenReturn(new ScanResult("job-1", "corp", Instant.now()));

    mockMvc
        .perform(post("/api/v1/inventory/scan")
            .contentType(MediaType.APPLICATION_JSON)
            .content("{\"forest\":\"corp\"}"))
        .andExpect(status().isAccepted())
        .andExpect(jsonPath("$.jobId").value("job-1"));
  }
}
Adnotacja / narzędzieDo czego
@Test, @ParameterizedTestprzypadki testowe
@Mock, @InjectMocksatrapy i wstrzyknięcie (Mockito)
assertThat(...) (AssertJ)czytelne asercje
@WebMvcTest + MockMvctest kontrolera bez pełnego kontekstu
@SpringBootTesttest integracyjny z pełnym kontekstem

Wskazówka: @SpringBootTest ładuje cały kontekst — jest wolny. Używaj go do testów integracyjnych, a do jednostek wybieraj @WebMvcTest / czysty Mockito bez Springa.


JUnit 5 + Mockito + AssertJ to nasz standardowy zestaw. Szybkie jednostki na logice serwisów plus kilka testów webowych dają pewność przy każdym PR-ze — i są warunkiem wejścia do głównej gałęzi.