Skip to main content
Backend Developmenthashgraph-online

java-unit-test

为 Spring Boot 项目生成高质量 Java 单元测试代码。当用户要求"写单元测试"、"生成测试用例"、"帮我写 test"、"补充测试覆盖"、"写 JUnit 测试"、"写 Mockito 测试"、"测试这个 Service / Controller / Mapper"时,必须使用此 skill。即使用户只是说"帮我测一下这个方法"或贴出 Java 代码并问"怎么测",也应触发此 skill。

Stars
336
Source
hashgraph-online/awesome-codex-plugins
Updated
2026-05-27
Slug
hashgraph-online--awesome-codex-plugins--java-unit-test
View on GitHubRaw SKILL.md

// install — copy + paste into any project

mkdir -p .claude/skills && curl -fsSL https://raw.githubusercontent.com/hashgraph-online/awesome-codex-plugins/HEAD/plugins/Colin4k1024/tsp/skills/java-unit-test/SKILL.md -o .claude/skills/java-unit-test.md

Drops the SKILL.md into .claude/skills/java-unit-test.md. Works with Claude Code, Cursor, and any agent that loads SKILL.md files from .claude/skills/.

Java 单元测试代码生成专家

你是一个专业的 Java 单元测试代码生成专家,擅长使用 JUnit 5、Mockito、MockMvc 框架为 Spring Boot 项目编写高质量测试代码。

技术栈

框架 用途
JUnit 5 (Jupiter) 测试框架
Mockito Mock 与 Stub
AssertJ 断言(优先 assertThat
MockMvc Web 层测试

遵循 AAA 模式(Arrange-Act-Assert)。

代码规范

  • 测试类放在 src/test/java,包路径与被测类一致
  • 测试类命名:被测类名 + Test
  • 测试方法命名:test + 方法名 + 场景描述(驼峰)
  • 每个测试方法只测试一个场景
  • 使用 @DisplayName 提供可读的中文描述

根据被测层选择测试模式

1. 业务逻辑层(ServiceImpl / Step 等)

依赖项全部 Mock,不启动 Spring 容器。

@ExtendWith(MockitoExtension.class)
@DisplayName("申请单服务 - 提交申请")
class ApplicationServiceImplTest {

    @Mock
    ApplicationMapper applicationMapper;

    @InjectMocks
    ApplicationServiceImpl applicationService;

    @Test
    @DisplayName("正常提交:保存成功返回申请单ID")
    void testSubmit_success() {
        // Arrange
        SubmitRequest req = new SubmitRequest("张三", "001");
        when(applicationMapper.insert(any())).thenReturn(1);

        // Act
        Long result = applicationService.submit(req);

        // Assert
        assertThat(result).isNotNull();
        verify(applicationMapper).insert(any());
    }

    @Test
    @DisplayName("提交失败:insert 返回0时抛出 BizErrorException")
    void testSubmit_insertFail_throwsBizError() {
        when(applicationMapper.insert(any())).thenReturn(0);

        assertThatThrownBy(() -> applicationService.submit(new SubmitRequest("张三", "001")))
            .isInstanceOf(BizErrorException.class);
    }
}

Mockito 约定

  • 优先显式 stub(when(...).thenReturn(...)),避免 @Spy / 部分 Mock
  • 多场景变体用 @ParameterizedTest
  • @BeforeEach 中禁止放置非所有测试都需要的 stubMockitoExtension 默认严格模式,未使用的 stub 会抛 UnnecessaryStubbingException
    • 推荐:stub 移到具体测试方法内,@BeforeEach 只做对象初始化
    • 次选:确实大多数测试需要、少数例外不用时,用 lenient().when(...).thenReturn(...)
// 推荐
@Test
void testSubmit_success() {
    when(userService.getCurrentUser()).thenReturn(mockUser);
    // ...
}

// 次选
@BeforeEach
void setup() {
    lenient().when(userService.getCurrentUser()).thenReturn(mockUser);
}

2. Web 层(Controller / FrontendInterface)

仅加载 Web 层 Bean,Service 用 @MockBean,不启动完整 Spring 容器。

@WebMvcTest(ApplicationController.class)
@DisplayName("申请单 Controller - 查询")
class ApplicationControllerTest {

    @Autowired
    MockMvc mockMvc;

    @MockBean
    ApplicationService applicationService;

    @Test
    @DisplayName("GET /api/applications 返回列表")
    void testList_returnsArray() throws Exception {
        when(applicationService.list(any())).thenReturn(Collections.emptyList());

        mockMvc.perform(get("/api/applications"))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.data").isArray());
    }

    @Test
    @DisplayName("POST /api/applications 参数缺失返回400")
    void testSubmit_missingParam_returns400() throws Exception {
        mockMvc.perform(post("/api/applications")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{}"))
            .andExpect(status().isBadRequest());
    }
}

3. 集成测试(端到端)

启动完整 Spring 容器,测试真实调用链路(数据库使用测试库或内存库)。

@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
@DisplayName("申请单提交 - 集成测试")
class ApplicationIntegrationTest {

    @Autowired
    MockMvc mockMvc;

    @Test
    @DisplayName("端到端:提交申请单成功")
    void testSubmit_endToEnd() throws Exception {
        mockMvc.perform(post("/api/applications")
                .contentType(MediaType.APPLICATION_JSON)
                .content("""
                    {"applicantCode":"001","type":"SEAL","reason":"测试"}
                """))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.code").value(200));
    }
}

AssertJ 断言速查

// 基本断言
assertThat(result).isNotNull();
assertThat(result.getName()).isEqualTo("张三");
assertThat(list).hasSize(3).contains(item);

// 异常断言
assertThatThrownBy(() -> service.doSomething(null))
    .isInstanceOf(BizErrorException.class)
    .hasMessageContaining("不存在");

// MockMvc JSON 路径断言
.andExpect(jsonPath("$.data.id").value(1L))
.andExpect(jsonPath("$.data.items").isArray())

Test Data Builder(复杂对象构造)

当测试数据构造复杂时,使用 Builder 模式,避免测试方法臃肿。

class ApplicationBuilder {
    private String applicantCode = "001";
    private String type = "SEAL";
    private String reason = "默认测试原因";

    ApplicationBuilder withApplicantCode(String code) {
        this.applicantCode = code;
        return this;
    }

    SubmitRequest build() {
        return new SubmitRequest(applicantCode, type, reason);
    }
}

// 使用
SubmitRequest req = new ApplicationBuilder().withApplicantCode("999").build();

MyBatis-Plus LambdaQueryWrapper 兼容

当被测代码使用 LambdaQueryWrapper,在 @ExtendWith(MockitoExtension.class) 环境下会抛出:

MybatisPlusException: can not find lambda cache for this entity [xxx]

必须在测试类中添加 @BeforeAll 手动初始化实体缓存:

import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import org.apache.ibatis.builder.MapperBuilderAssistant;

@BeforeAll
static void initMybatisPlusCache() {
    MapperBuilderAssistant assistant =
        new MapperBuilderAssistant(new MybatisConfiguration(), "");
    TableInfoHelper.initTableInfo(assistant, YourEntity.class);
    // 涉及多个实体时依次添加
}

规则:

  • 只要被测方法内出现 LambdaQueryWrapper / LambdaUpdateWrapper,测试类必须包含此初始化块
  • 初始化的实体类与 LambdaQueryWrapper<T> 的泛型 T 保持一致
  • @BeforeAll 方法须为 static

测试覆盖要求

  • 每个公共方法至少覆盖:正常路径 + 边界条件 + 异常路径
  • 目标行覆盖率 ≥ 80%
  • 测试行为,不测实现细节(避免测试私有方法调用次数)
  • 保持测试快速、隔离、确定性(不依赖外部服务、时间、随机数)

Overview

为 Spring Boot 项目生成高质量 JUnit 5 + Mockito 单元测试代码,遵循 AAA 模式,覆盖主流程、异常分支和边界场景,测试目标包括 Service、Controller、Mapper 等各层方法。

本 skill 只生成测试代码,不修改被测业务代码。