junit-testing — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited junit-testing (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Patterns for writing effective unit, integration, and slice tests in Java.
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
class UserServiceTest {
private UserService userService;
private UserRepository userRepository;
@BeforeEach
void setUp() {
userRepository = new InMemoryUserRepository();
userService = new UserService(userRepository);
}
@Test
@DisplayName("creates a user with valid input")
void createsUserWithValidInput() {
User result = userService.createUser(new CreateUserRequest("Alice", "[email protected]"));
assertAll(
() -> assertNotNull(result.getId()),
() -> assertEquals("Alice", result.getName()),
() -> assertEquals("[email protected]", result.getEmail())
);
}
@Test
@DisplayName("throws when email is already taken")
void throwsWhenEmailTaken() {
userRepository.save(new User("Bob", "[email protected]"));
assertThrows(ConflictException.class,
() -> userService.createUser(new CreateUserRequest("Bob2", "[email protected]")));
}
@Nested
@DisplayName("when user exists")
class WhenUserExists {
private User existingUser;
@BeforeEach
void setUp() {
existingUser = userRepository.save(new User("Alice", "[email protected]"));
}
@Test
void findsById() {
Optional<User> found = userService.findById(existingUser.getId());
assertTrue(found.isPresent());
assertEquals("Alice", found.get().getName());
}
}
}Isolate the unit under test by mocking its collaborators.
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.*;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock private OrderRepository orderRepository;
@Mock private PaymentGateway paymentGateway;
@InjectMocks private OrderService orderService;
@Test
@DisplayName("places order and charges payment")
void placesOrderAndChargesPayment() {
var order = new Order("product-1", 2, 29_99L);
when(orderRepository.save(any(Order.class))).thenReturn(order);
when(paymentGateway.charge(anyLong())).thenReturn(new PaymentResult(true, "txn-123"));
assertNotNull(orderService.placeOrder(order));
verify(paymentGateway).charge(29_99L);
verify(orderRepository).save(order);
}
@Test
@DisplayName("rolls back order when payment fails")
void rollsBackWhenPaymentFails() {
when(paymentGateway.charge(anyLong())).thenReturn(new PaymentResult(false, null));
assertThrows(PaymentFailedException.class,
() -> orderService.placeOrder(new Order("product-1", 1, 50_00L)));
verify(orderRepository, never()).save(any());
}
}Test controllers in isolation without starting the full application context. Only the web layer is loaded.
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired private MockMvc mockMvc;
@MockBean private UserService userService;
@Test
@DisplayName("GET /api/v1/users/{id} returns user")
void getUser() throws Exception {
when(userService.findById("1"))
.thenReturn(Optional.of(new UserResponse("1", "Alice", "[email protected]")));
mockMvc.perform(get("/api/v1/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.name").value("Alice"));
}
@Test
@DisplayName("POST /api/v1/users validates request body")
void validateCreateUser() throws Exception {
mockMvc.perform(post("/api/v1/users")
.contentType("application/json")
.content("{\"name\":\"\",\"email\":\"not-an-email\"}"))
.andExpect(status().isBadRequest());
}
}Spin up real database containers for integration tests. Tests are reliable and match production behavior.
@SpringBootTest
@Testcontainers
class UserRepositoryIntegrationTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16-alpine")
.withDatabaseName("testdb").withUsername("test").withPassword("test");
@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}
@Autowired private UserRepository userRepository;
@Test
@DisplayName("persists and retrieves a user")
void persistsAndRetrieves() {
User saved = userRepository.save(new User("Alice", "[email protected]"));
Optional<User> found = userRepository.findById(saved.getId());
assertTrue(found.isPresent());
assertEquals("Alice", found.get().getName());
}
}Run the same test logic with different inputs. Reduces duplication.
class EmailValidatorTest {
private final EmailValidator validator = new EmailValidator();
@ParameterizedTest
@CsvSource({"[email protected], true", "[email protected], true", "invalid, false", "@no-local.com, false"})
void validatesEmail(String email, boolean expected) {
assertEquals(expected, validator.isValid(email));
}
@ParameterizedTest
@NullAndEmptySource
@ValueSource(strings = {" ", "\t"})
void rejectsBlankEmails(String email) {
assertFalse(validator.isValid(email));
}
@ParameterizedTest
@MethodSource("validEmailProvider")
void acceptsValidEmails(String email) { assertTrue(validator.isValid(email)); }
static Stream<String> validEmailProvider() {
return Stream.of("[email protected]", "[email protected]", "[email protected]");
}
}| Category | Key APIs |
|---|---|
| Lifecycle | @BeforeEach, @AfterEach, @BeforeAll, @AfterAll, @Nested |
| Assertions | assertEquals, assertTrue, assertThrows, assertAll, assertTimeout |
| Mockito | when().thenReturn(), verify(), any(), @Mock, @InjectMocks |
| Slices | @WebMvcTest (controllers), @DataJpaTest (JPA), @SpringBootTest (full) |
| Parameterized | @CsvSource, @ValueSource, @MethodSource, @NullAndEmptySource |
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.