name: unit-testing description: "Writes and maintains unit tests for Java Spring projects using JUnit and Mockito. For testing business logic in services and utilities."
Unit Testing Skill
When asked to write or maintain unit tests for a Java Spring workshop, follow these guidelines.
When to Write Unit Tests
- New services/utilities: Add tests when creating new service or utility classes.
- Bug fixes: Include tests that reproduce the bug and assert the fix.
- Complex logic: Test edge cases, boundary conditions, and error handling.
- Refactoring: Ensure tests pass before and after refactorings.
File Naming and Location
- Test source roots: Put tests under
src/test/java(andsrc/test/resourcesfor fixtures). - Naming convention: Use
ClassNameTest.javaorClassNameTests.java(e.g.,UserServiceTest). - Colocate: Keep the test package mirroring the main source package for easy navigation.
Test Frameworks and Tools
- Test runner: JUnit 5 (Jupiter)
- Mocking: Mockito (use
@ExtendWith(MockitoExtension.class)or@MockBeanfor Spring tests) - Assertions: JUnit assertions plus AssertJ for fluent assertions
- Coverage: JaCoCo (integrated via Maven or Gradle)
Test Structure
Follow Arrange — Act — Assert. Example structure with JUnit 5 + Mockito:
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
void createUser_savesSuccessfully() {
// Arrange
User user = new User("alice@example.com");
when(userRepository.save(any())).thenReturn(user);
// Act
User result = userService.createUser(user);
// Assert
assertThat(result.getEmail()).isEqualTo("alice@example.com");
verify(userRepository, times(1)).save(any());
}
}
For Spring slice tests where you need Spring context, use @WebMvcTest, @DataJpaTest, or @SpringBootTest as appropriate. Prefer lightweight slice tests when possible.
Mocking Dependencies
- Use Mockito for unit tests:
@Mock+@InjectMocksorMockito.when(...).thenReturn(...). - For Spring integration tests, use
@MockBeanto replace a Spring bean in the context. - Use
ArgumentCaptorto assert arguments passed to collaborators.
Example usage with Mockito:
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock
OrderRepository repo;
@InjectMocks
OrderService service;
@Test
void calculateTotal_usesDiscounts() {
when(repo.findById(1L)).thenReturn(Optional.of(new Order(...)));
// Act
BigDecimal total = service.calculateTotal(1L);
// Assert
assertThat(total).isEqualByComparingTo(new BigDecimal("42.00"));
}
}
Running Tests
- Maven:
mvn test(unit tests),mvn verify(runs checks and coverage if configured) - Gradle:
./gradlew test - IDE: Use IntelliJ/VS Code test runners for faster feedback and debugging
- Coverage report:
mvn jacoco:reportor./gradlew jacocoTestReport
Note: Java ecosystem doesn't have a watch mode identical to JS; use IDE test runners or Gradle continuous build (--continuous) for faster iteration.
Coverage Expectations
- Services/utilities: Aim for >80% on critical modules — prefer meaningful coverage over percentage chasing.
- Edge cases: Test boundary conditions (nulls, empty lists, max/min values).
- Error paths: Verify validation, exception handling, and retries.
Best Practices
- Test behavior, not implementation: Focus on contract and observable outcomes.
- Small, focused tests: One logical assertion per test (grouped checks are OK when related).
- Clear test names:
shouldDoX_whenY()orcreateUser_savesSuccessfully(). - Isolated tests: Use mocks/stubs to avoid external I/O; keep tests fast.
- Use fixtures: Centralize test data creation (builders, test factories) to reduce duplication.
Common JUnit/Mockito Patterns
- Assertions:
Assertions.assertEquals, AssertJassertThat(...) - Exception testing:
assertThrows(IllegalArgumentException.class, () -> ...) - Verify interactions:
verify(mock).method(args)andverify(mock, times(1))... - Argument captors:
@Captor ArgumentCaptor<Type> captor; verify(mock).save(captor.capture());
Example: exception assertion
@Test
void shouldThrow_whenInputInvalid() {
assertThrows(IllegalArgumentException.class, () -> service.process(null));
}
Reference
Recommended docs and tools:
- JUnit 5: https://junit.org/junit5/
- Mockito: https://site.mockito.org/
- AssertJ: https://assertj.github.io/doc/
- JaCoCo: https://www.eclemma.org/jacoco/
When running a Java Spring workshop, provide example projects with a pom.xml or build.gradle that include JUnit, Mockito, and JaCoCo so students can run mvn test or ./gradlew test immediately.