Java/05core12 min

JUnit 5 + Mockito tests

ProfessNet tests Java code with JUnit 5 (Jupiter), with Mockito for mocking and AssertJ for readable assertions. This lesson shows our patterns — from a pure unit to a Spring Boot web-layer test.

Anatomy of a JUnit 5 test

The AAA layout: 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");
  }
}

ProfessNet standard: the test method name describes the behavior (method_condition_result). Test classes and methods have default (package-private) access — in JUnit 5 they need not be public.

Parameterized tests

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

AssertJ — fluent assertions

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

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

ProfessNet standard: we write assertions with AssertJ (assertThat) — more readable and with a better error message than raw assertEquals.

Mockito — dependency isolation

@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 creates a stub, @InjectMocks injects it into the class under test.

Spring Boot web-layer tests

@WebMvcTest loads only the controller layer (fast) and mocks the dependencies.

@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"));
  }
}
Annotation / toolFor what
@Test, @ParameterizedTesttest cases
@Mock, @InjectMocksstubs and injection (Mockito)
assertThat(...) (AssertJ)readable assertions
@WebMvcTest + MockMvca controller test without the full context
@SpringBootTestan integration test with the full context

Tip: @SpringBootTest loads the whole context — it's slow. Use it for integration tests, and for units choose @WebMvcTest / pure Mockito without Spring.


JUnit 5 + Mockito + AssertJ is our standard toolkit. Fast units on service logic plus a few web tests give confidence with every PR — and are an entry condition for the main branch.