AgentSkillsCN

testing-junit

结合 Mockito 和 AssertJ 的 JUnit 5 测试模式

SKILL.md
--- frontmatter
name: testing-junit
description: JUnit 5 testing patterns with Mockito and AssertJ
keywords:
  - junit
  - junit5
  - test
  - testing
  - mockito
  - mock
  - stub
  - assertj
  - assertions
  - parameterized
  - integration test
filePatterns:
  - "*.java"
  - "pom.xml"
  - "build.gradle"
  - "build.gradle.kts"
frameworks:
  - junit
  - mockito
  - assertj
tokenCount: 1600
version: 1.0.0

Testing with JUnit 5, Mockito, AssertJ

Practical testing patterns for Java services and APIs. Prefer readable tests over clever tests.

Selective Reading Rule

Read ONLY files relevant to the request! Use the content map to focus.


Content Map

FileDescriptionWhen to Read
junit-basics.md@Test, assertions, lifecycleUnit test setup
parameterized.md@ParameterizedTest, sourcesData-driven tests
mockito.md@Mock, @InjectMocks, stubbingIsolated unit tests
assertj.mdFluent assertions, collectionsReadable assertions
spring-slices.md@WebMvcTest, @DataJpaTestFramework-level tests

Core Patterns

1. JUnit 5 Structure

java
@ExtendWith(MockitoExtension.class)
class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService;

    @Test
    void findById_whenExists_returnsUser() {
        var user = User.create("u1", "dev@example.com");
        when(userRepository.findById("u1")).thenReturn(Optional.of(user));

        var result = userService.findById("u1");

        assertThat(result).isPresent();
        assertThat(result.get().getEmail()).isEqualTo("dev@example.com");
    }
}

2. Parameterized Tests

java
@ParameterizedTest
@ValueSource(strings = {"a@b.com", "user@company.io"})
void validateEmail_acceptsValid(String email) {
    var result = EmailValidator.isValid(email);
    assertThat(result).isTrue();
}

@ParameterizedTest
@CsvSource({
    "PENDING, true",
    "ACTIVE, false"
})
void isInactive_flagsStatus(String status, boolean expected) {
    assertThat(UserStatus.valueOf(status).isInactive()).isEqualTo(expected);
}

3. Mockito Stubbing and Verification

java
@Test
void createUser_savesEntity() {
    var request = new CreateUserRequest("dev@example.com", "Dev");
    when(userRepository.existsByEmail(request.email())).thenReturn(false);

    userService.create(request);

    verify(userRepository).save(any(User.class));
}

@Test
void updateUser_throwsWhenMissing() {
    when(userRepository.findById("missing")).thenReturn(Optional.empty());

    assertThatThrownBy(() -> userService.update("missing", new UpdateUserRequest("name")))
        .isInstanceOf(ResourceNotFoundException.class)
        .hasMessageContaining("User");
}

4. AssertJ for Readability

java
assertThat(users)
    .hasSize(3)
    .extracting(User::getEmail)
    .contains("dev@example.com");

assertThat(response)
    .returns(HttpStatus.CREATED, ResponseEntity::getStatusCode)
    .extracting(ResponseEntity::getBody)
    .isNotNull();

5. Test Lifecycle and Organization

java
@Nested
class CreateUserTests {

    @Test
    void rejectsDuplicateEmail() { }

    @Test
    void hashesPassword() { }
}

@BeforeEach
void setup() {
    // Use only for shared, simple setup
}

Decision Checklist

  • Test name explains behavior? (when/then or should format)
  • Only one reason to fail?
  • Mocks only for external dependencies?
  • Assertions are readable?
  • Parameterized test when data-driven?

Anti-Patterns

Anti-PatternWhy BadBetter Approach
Testing private methodsRefactoring painTest public behavior
Mocking everythingBrittle testsMock only boundaries
Multiple behaviors per testHard to debugSplit into focused tests
Asserting via logsNot deterministicAssert on outcomes

Related Skills

NeedSkill
Java patterns@[skills/java-expert]
Build tools@[skills/maven-gradle]