1. 老旧项目补测试不是“加功能”而是给系统做一次精准外科手术你有没有遇到过这样的项目代码库年龄比团队里最资深的工程师还大核心模块用的是十年前的Spring 2.5 Hibernate 3.2组合Maven依赖树里混着三个不同版本的commons-langpom.xml里甚至还有scopeprovided/scope指向早已下线的内部私服。没人敢动支付回调逻辑因为上一次修改导致了三天后才被发现的订单状态错乱没人敢删掉那个叫UtilsHelperV2FinalNew.java的类因为六个模块都通过反射调用了它第47行的一个静态方法——而这个方法的注释写着“临时方案下周重构”写于2015年。这就是我接手的“教育SaaS后台v1.2”——一个上线八年、迭代超200次、文档缺失率83%、单元测试覆盖率长期卡在20%的典型遗留系统。20%不是指“没写完”而是指整个项目里只有登录鉴权模块有12个JUnit 4测试用例其余所有业务逻辑——课程排期、优惠券核销、学情同步、消息推送——全部裸奔。CI流水线里那行绿色的Coverage: 20.3%像一道耻辱柱每次构建成功都带着讽刺意味。但这次我没选择重写也没申请三个月停机时间做“测试基建”。我决定用AI当主刀医生对存量代码做一次靶向式单元测试生成手术不碰业务逻辑不动接口契约只在最小侵入前提下为关键路径补上可验证、可回归、可演进的测试覆盖。目标很具体——把核心业务包com.edusys.biz.*的行覆盖率从20%提升到80%且保证所有新增测试能通过、能定位、能维护。这不是炫技是生存需求下季度要接入新支付网关没有测试护城河每一次上线都是拆弹。关键词里反复出现的JUnit、Mockito、覆盖率恰恰点破了这场手术的三大支柱框架选型决定落地成本模拟策略决定隔离质量覆盖率指标决定价值闭环。而AI在这里不是魔法棒它是个高强度协作者——它帮我读千行代码、猜十种边界、写百个断言但我必须亲手校验每处when(...).thenReturn(...)是否真实反映被测对象的协作契约必须确认每个Test里的assertThat是否真正守护了业务语义。这活儿AI干得快人干得准人定方向AI填细节人守底线AI扩产能。2. 为什么不用“AI一键生成全量测试”——三道不可逾越的临床红线市面上不少工具宣传“AI全自动补测试”输入代码输出.java测试文件覆盖率数字蹭蹭上涨。我在第一周就试了三款主流IDE插件含某知名IDE内置AI助手结果令人警醒它们生成的测试92%无法编译67%通过但无业务意义剩下31%中又有24%在两周后因一次微小重构彻底失效。这不是AI不行而是把测试生成当成代码翻译犯了根本性认知错误。我总结出三条必须死守的临床红线它们直接决定了这场手术能否成功2.1 红线一绝不生成“假阳性”测试——覆盖率数字≠质量保障AI最擅长的事恰恰是最危险的事它能轻松写出assertEquals(1, service.calculate(1,1))这种语法正确、运行通过的测试。但如果calculate方法本意是计算“用户剩余课时”而AI基于参数名a,b猜成“整数加法”这个测试就是毒药——它让开发者误以为逻辑受保护实则完全偏离业务。我在CourseScheduleService上栽过跟头AI生成了23个关于scheduleTime的测试全部假设它是LocalDateTime类型并校验格式而实际代码里它是个String存储的是“2023-10-01T08:00:0008:00”格式的ISO字符串。这些测试全绿但对真实业务场景零防护。我的铁律是每个断言必须对应一个明确的业务规则或契约声明。比如“排课时间不能早于当前时间”我就强制AI在生成前先提取方法Javadoc里的throws IllegalArgumentException if scheduleTime is in the past再据此构造assertThrows断言。没有显式契约的代码宁可不测也不造“幻觉测试”。2.2 红线二绝不绕过“可测性改造”——硬塞测试不如先修路老旧项目最大的坑是大量静态方法、单例全局状态、new对象硬编码。比如PaymentProcessor里直接new AlipayClient()UserServiceImpl里调用DateUtils.getCurrentDate()静态方法。AI可以帮你mockAlipayClient但它无法解决new操作本身带来的耦合。我试过让AI在测试里用PowerMockito mock构造函数结果生成的测试在Java 11环境下全部失败PowerMockito 2.x不兼容模块化。更糟的是这类测试极度脆弱——只要AlipayClient构造参数变个顺序所有测试崩盘。我的方案是用AI辅助“可测性微创手术”而非硬扛。我让AI扫描所有new XXX()调用自动生成重构建议把new AlipayClient()封装进AlipayClientFactory接口原类改用Autowired注入。AI生成工厂实现和Spring配置我只需审核两处1工厂是否真解耦了密钥等敏感参数2注入点是否覆盖了所有调用路径。一周内我们为7个核心类完成了可测性改造代价是修改了12个类的37行代码换来的是后续AI生成的测试全部基于标准Mockito稳定度提升400%。2.3 红线三绝不信任“黑盒覆盖率”——必须穿透到结构层验证很多工具报告“覆盖率80%”你点开diff-cover报告才发现这80%全是if/else分支覆盖而try/catch里的异常处理路径、switch的default分支、甚至for循环的空集合边界全部未覆盖。更隐蔽的是lcov统计的“行覆盖”陷阱一行return a b ? processA() : processB();AI只要让ab为true/false各跑一次就算“该行已覆盖”但processA()和processB()内部逻辑可能完全没执行。我坚持用三重覆盖率交叉验证行覆盖Line Coverage用JaCoCo看基础执行流分支覆盖Branch Coverage强制AI为每个if、while、?:生成true/false双路径测试突变覆盖Mutation Coverage用PITest把a b改成a b看测试是否失败——失败才证明测试真懂业务逻辑。第一次跑PITest时突变存活率高达65%意味着65%的代码缺陷不会被现有测试发现这直接暴露了AI生成测试的“表面功夫”本质。我把PITest报告按包分发给开发要求每个模块突变存活率15%才能合入。这倒逼AI生成的测试必须深入业务语义比如为CouponValidator生成测试时AI不再只测“满100减10”而是必须覆盖“满100减10但用户余额不足”、“满100减10但商品已下架”等真实失败场景。提示别被“80%覆盖率”数字迷惑。我见过覆盖率95%的模块线上仍频繁报NullPointerException——因为所有测试都用Mock创建对象却忘了验证service.init()是否被调用。覆盖率是结果不是目标可维护、可定位、可演进的测试资产才是手术成功的唯一标准。3. AI协同工作流从代码切片到可交付测试的七步闭环把AI当实习生用它会给你一堆半成品把它当资深搭档用你得设计一套严谨的协同协议。我打磨出的七步闭环不是线性流程而是带反馈的螺旋上升每一步的输出都成为下一步的输入与校验依据。这套流程让我在两个月内为32个核心类补上了117个高质量JUnit 5测试类平均每个类3.6个测试方法核心业务包行覆盖率从20%升至82.3%分支覆盖率从18%升至76.5%。3.1 步骤一精准切片——用AST解析锁定“高危低覆盖”代码块AI不是万能阅读器喂给它整个OrderService.java2300行它大概率会忽略第1842行那个嵌套在try-catch-finally里的updateStatus()调用。我的做法是先用JavaParser解析源码生成AST再用规则引擎筛选高危节点。规则很简单方法体行数 50包含if/for/while/try等控制流关键字Javadoc缺失或少于2行调用外部服务含RestTemplate、JdbcTemplate、new非POJO对象所在类的JaCoCo历史覆盖率 40%。工具链javaparser-core 自定义CoverageAnalyzer。结果从127个候选类中精准锁定21个“手术优先级最高”的类如OrderRefundProcessor退款核心覆盖率仅12%、CourseEnrollmentManager选课并发覆盖率0%。AI只处理这21个类效率提升3倍噪声归零。3.2 步骤二契约萃取——让AI读懂“这段代码到底承诺了什么”给AI喂代码它看到的是语法树给人看代码我们看到的是契约。我的关键动作是强制AI从代码中反向推导出三类契约并人工校验。输入契约参数约束如NotNull,Min(1),Pattern以及隐式约束如String userId实际要求16位UUID行为契约方法副作用如updateOrderStatus()必改数据库order_status字段、异常抛出throws InsufficientBalanceException输出契约返回值语义如ListCourseVO中每个VO的status字段只能是ACTIVE/EXPIRED/PENDING。我用正则AST遍历提取显式契约再让AI分析方法体逻辑推导隐式契约。例如refundAmount(BigDecimal amount)方法AI分析出“当amount为负数时抛出IllegalArgumentException当amount大于订单实付金额时抛出RefundExceededException”。这成为后续生成测试用例的黄金准则——每个测试必须验证至少一条契约。3.3 步骤三边界建模——用决策表驱动AI生成“有意义”的测试数据AI生成随机数refundAmount(new BigDecimal(123.45))毫无价值。我的方案是把业务规则转化为决策表再让AI按表生成测试用例。以CouponValidator.validate(Coupon coupon, Order order)为例我手写决策表coupon.typeorder.totalcoupon.minSpend期望结果业务理由DISCOUNT99.99100.00false订单未达门槛DISCOUNT150.00100.00true达门槛可用FREE_SHIPPING0.000.00true包邮券无门槛EXPIRED200.00100.00false券已过期AI的任务很明确为每行决策生成符合列条件的具体对象实例。它自动创建Coupon.builder().type(DISCOUNT).minSpend(new BigDecimal(100.00)).build()并确保order.total精确为99.99。这避免了AI“脑补”无效数据每个测试都直击业务要害。3.4 步骤四Mock策略生成——让AI设计“恰到好处”的隔离边界Mock不是越多越好而是“最少必要”。AI常犯的错是为OrderService里一个简单getOrderById()调用也去Mock整个OrderRepository。我的指令是只Mock“跨边界”调用且必须声明协作意图。数据库访问JdbcTemplate,EntityManager→ MockJdbcOperations或CrudRepository外部HTTP服务RestTemplate,WebClient→ MockRestTemplate或用WireMock静态工具类DateUtils,JsonUtils→ 用Mockito.mockStatic()Java 17同包内服务调用 →不Mock用SpyBean或真实对象集成测试。AI生成的Mock代码必须包含注释说明“为何Mock此对象”例如// Mock AlipayClient: 因其调用真实支付宝网关需隔离网络依赖。这倒逼开发者思考依赖本质。3.5 步骤五断言精炼——用“业务语言”替代“技术断言”assertEquals(expected, actual)是技术断言assertThat(order.getStatus()).isEqualTo(ORDER_REFUNDED)是业务断言。AI生成前者我强制它升级为后者。我的技巧是让AI从方法名和返回值类型推导业务语义。方法名含is*、can*、should*→ 生成布尔断言如assertTrue(user.canAccessCourse())返回OrderStatus枚举 → 断言isEqualTo(ORDER_PAID)而非equals(PAID)返回ResultT包装类 → 断言isSuccess()和getData()双重校验。这使测试代码自带业务文档属性新人看测试就能懂业务规则。3.6 步骤六PITest驱动的突变验证——用“制造缺陷”来检验测试质量每轮AI生成测试后立即跑PITest。我设置阈值单个测试类突变存活率 20%则该类所有测试打回重写。常见失败案例AI为calculateDiscount()生成测试只验证discount 0但PITest把0变成0后测试仍通过——说明没覆盖“折扣为0”的边界AI为sendNotification()生成测试只验证notificationService.send()被调用但PITest把send()方法体置空后测试仍通过——说明没验证发送结果如返回值、状态更新。AI根据PITest报告自动补充缺失的断言。这步让测试质量从“能跑通”跃升至“真可靠”。3.7 步骤七人工终审与知识沉淀——把AI产出转化为团队资产AI生成的测试必须经过三人小组终审业务方确认断言是否符合真实业务规则如“满100减10”是否包含运费架构师确认Mock策略是否合理有无过度隔离QA工程师确认测试是否覆盖线上高频报错场景。终审通过的测试AI自动生成《测试覆盖说明书》该类覆盖了哪些业务规则引用需求文档ID未覆盖的边界及原因如“跨境支付汇率转换未覆盖因依赖第三方服务计划Q4用Contract Test替代”关键Mock点说明如“AlipayClient已Mock但AlipayNotifyHandler未Mock因其逻辑简单且无外部依赖”。这份文档随代码入库成为团队共有的测试知识库。4. 工具链实战配置从IntelliJ到CI/CD的无缝集成再好的流程没有趁手的工具链就是纸上谈兵。我搭建的这套环境目标就一个让AI协同测试生成像写普通代码一样自然像提交PR一样标准。所有配置均适配老旧项目Spring Boot 2.1, Java 8, Maven 3.5无需升级基础框架。4.1 IDE层IntelliJ 自研AI插件非市场版市场上的AI插件如GitHub Copilot、Tabnine对测试生成支持薄弱常把Test写成testMockito.mock()写成new Mock()。我基于IntelliJ Platform SDK开发了轻量插件TestSurgeon核心能力上下文感知光标停在OrderService.java的refund()方法上右键Generate Test with AI自动提取该方法AST、Javadoc、调用栈模板驱动预置JUnit 5 Mockito AssertJ模板AI只填充given/when/then块实时校验生成过程中实时调用本地JaCoCo agent分析目标类覆盖率缺口提示“此处缺少null参数测试”一键PITest生成后自动触发PITest分析红色突变点高亮显示。配置要点在idea.properties中增加idea.max.intellisense.filesize5000避免大文件卡顿vmoptions添加-XX:MaxRAMPercentage75.0保障AI模型加载内存。4.2 构建层Maven多阶段精准控制老旧项目用Maven我拒绝“一刀切”式配置。pom.xml关键片段profiles !-- 仅用于AI生成测试的开发阶段 -- profile idai-test-gen/id dependencies dependency groupIdorg.junit.jupiter/groupId artifactIdjunit-jupiter/artifactId version5.8.2/version scopetest/scope /dependency dependency groupIdorg.mockito/groupId artifactIdmockito-core/artifactId version4.11.0/version scopetest/scope /dependency !-- 引入PITest但仅在ai-test-gen profile激活 -- dependency groupIdorg.pitest/groupId artifactIdpitest-junit5-plugin/artifactId version1.9.11/version scopetest/scope /dependency /dependencies /profile /profiles build plugins !-- JaCoCo精准指定要分析的包 -- plugin groupIdorg.jacoco/groupId artifactIdjacoco-maven-plugin/artifactId version0.8.8/version configuration includes includecom/edusys/biz/**/include includecom/edusys/service/**/include /includes excludes exclude**/dto/**/exclude exclude**/config/**/exclude /excludes /configuration /plugin !-- PITest聚焦核心业务包跳过DTO/Config -- plugin groupIdorg.pitest/groupId artifactIdpitest-maven/artifactId version1.9.11/version configuration targetClasses paramcom.edusys.biz.*/param paramcom.edusys.service.*/param /targetClasses targetTests paramcom.edusys.biz.*Test/param /targetTests mutationThreshold85/mutationThreshold !-- 全局存活率阈值 -- outputFormats paramHTML/param /outputFormats /configuration /plugin /plugins /build关键点includes和targetClasses严格限定范围避免AI生成的测试污染其他模块mutationThreshold设为85即允许15%突变存活留出合理容错空间。4.3 CI/CD层GitLab CI流水线中的质量门禁在gitlab-ci.yml中我设置了三道质量门禁任何一项失败PR自动拒绝stages: - test - quality-gate generate-tests: stage: test image: maven:3.8-openjdk-11 script: - mvn clean test-compile -Pai-test-gen # 编译测试类 - mvn org.jacoco:jacoco-maven-plugin:prepare-agent test -Pai-test-gen # 运行测试并收集覆盖率 artifacts: paths: - target/site/jacoco/ pitest-analysis: stage: quality-gate image: maven:3.8-openjdk-11 script: - mvn org.pitest:pitest-maven:mutationCoverage -Pai-test-gen artifacts: paths: - target/pit-reports/ coverage-check: stage: quality-gate image: python:3.9 script: - pip install pytest-cov - python -c import xml.etree.ElementTree as ET; tree ET.parse(target/site/jacoco/jacoco.xml); root tree.getroot(); coverage float(root.find(.//package[name\com.edusys.biz\]).find(.//counter[type\LINE\]).attrib[covered]) / float(root.find(.//package[name\com.edusys.biz\]).find(.//counter[type\LINE\]).attrib[total]); print(fBusiness package line coverage: {coverage:.2%}); assert coverage 0.80, fCoverage {coverage:.2%} 80% threshold; allow_failure: false pitest-check: stage: quality-gate image: python:3.9 script: - python -c import json; with open(target/pit-reports/index.json) as f: data json.load(f); survival_rate data[statistics][survivingMutations] / data[statistics][totalMutations]; print(fMutation survival rate: {survival_rate:.2%}); assert survival_rate 0.15, fSurvival rate {survival_rate:.2%} 15% threshold; allow_failure: false这套配置实现了generate-tests确保AI生成的测试能编译、能运行pitest-analysis生成突变报告供人工复盘coverage-check行覆盖率硬性门禁≥80%pitest-check突变存活率硬性门禁≤15%。所有门禁失败GitLab自动评论“Coverage gate failed: Business package line coverage 78.2% 80%. Please add tests for [class] and [method].” —— 直接定位到缺失点。4.4 团队协作层测试资产的可持续演进机制AI生成的测试不是终点而是起点。我建立了三项机制保障可持续性测试健康度看板用Grafana接入JaCoCo和PITest API每日更新各模块“覆盖率趋势”、“突变存活率”、“测试执行时长”。红色预警自动钉钉通知负责人测试债务登记簿每个PR合并时必须填写TEST_DEBT.md记录“本次未覆盖的边界及预计解决时间”。例如“CourseEnrollmentManager.enroll()未覆盖高并发场景预计Q4引入JMeter压测”。该文件由AI定期扫描生成待办清单AI训练反馈环将人工终审中驳回的AI生成测试含驳回理由存入ai-feedback-dataset每月用这些样本微调本地Llama 3-8B模型。效果驳回率从首月35%降至第三月8%。注意不要迷信“AI生成即完成”。我见过团队把AI生成的测试直接合入结果两周后因一次Transactional注解位置调整所有测试因事务未提交而集体失效。测试的生命力在于持续维护而维护的前提是清晰的契约和可理解的断言。AI负责“生”人负责“养”缺一不可。5. 血泪教训那些让覆盖率飙升却差点毁掉项目的坑从20%到80%数字很美但背后踩过的坑每一个都足以让项目停摆一周。这些不是教科书里的理论风险而是我在深夜盯着CI流水线红灯时用咖啡和焦虑换来的真知。分享其中三个最痛的教训它们直接决定了你的AI补测试之旅是走向成功还是滑向深渊。5.1 坑一Mock了不该Mock的“领域对象”导致测试通过但业务崩溃User实体类里有个getAge()方法逻辑是return Period.between(birthDate, LocalDate.now()).getYears()。AI看到LocalDate.now()立刻判定“需Mock时间”于是生成ExtendWith(MockitoExtension.class) class UserServiceTest { Test void shouldCalculateAgeCorrectly() { // 错误示范Mock了LocalDate类本身 try (MockedStaticLocalDate mocked Mockito.mockStatic(LocalDate.class)) { mocked.when(LocalDate::now).thenReturn(LocalDate.of(2023, 10, 1)); User user new User(); user.setBirthDate(LocalDate.of(2000, 1, 1)); assertEquals(23, user.getAge()); // 测试通过 } } }问题在哪LocalDate.now()是纯函数无副作用Mock它违反了“只Mock跨边界依赖”原则。更致命的是当User类被其他模块如ReportGenerator使用时getAge()在真实环境中调用LocalDate.now()而测试中Mock的now()只在测试线程生效——导致ReportGenerator的测试里getAge()返回错误年龄我的修复方案删除所有对LocalDate、Instant等不可变值对象的Mock对需要固定时间的场景统一注入ClockUser user new User(clock);测试中传入Clock.fixed(Instant.parse(2023-10-01T00:00:00Z), ZoneId.of(UTC))在pom.xml中添加dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-validation/artifactId/dependency用Past注解约束birthDate让校验逻辑前置。这个坑教会我AI的“安全”意识永远不如人对领域边界的敬畏。5.2 坑二AI生成的“完美测试”在CI环境里100%失败本地IntelliJ里AI生成的117个测试全部绿色推送到GitLab CI117个全红。日志只有一行java.lang.NoClassDefFoundError: org/mockito/Mockito。排查发现CI用的Docker镜像是maven:3.8-openjdk-11而本地IDE用的是openjdk:11-jre-slim两者CLASSPATH差异导致Mockito类加载失败。更隐蔽的是AI生成的测试用了Mockito.lenient()Mockito 4.11特性但CI镜像里Maven仓库缓存的是Mockito 3.12。我的根治方案在CI脚本开头强制清理本地仓库rm -rf ~/.m2/repository/org/mockito/pom.xml中锁定Mockito版本mockito.version4.11.0/mockito.version并在所有依赖中显式声明最关键一步让AI在生成测试时自动添加版本兼容性检查。我给AI的指令是“生成测试前先读取pom.xml中mockito.version若为4.x则可用lenient()若为3.x则改用when().thenAnswer()替代”。AI现在生成的每个测试类顶部都有注释// Generated for Mockito 4.11.0: uses lenient() for relaxed mocking。这个坑揭示真相本地开发环境的“宽容”是CI失败的最大温床。AI必须学会在目标环境中思考而非仅在IDE里呼吸。5.3 坑三覆盖率数字飙升但线上Bug率不降反升当覆盖率冲到82.3%我们庆祝了。三天后支付回调服务开始间歇性失败错误日志是NullPointerException堆栈指向PaymentCallbackHandler.process()第87行——而这一行AI生成的测试里明明有Mock PaymentService paymentService且when(paymentService.confirm()).thenReturn(true)。为什么NPE因为process()方法里paymentService是通过ApplicationContext.getBean(paymentService)动态获取的而AI Mock的paymentService只是局部变量从未注册到Spring容器测试在ExtendWith(MockitoExtension.class)下运行完全脱离Spring上下文。我的亡羊补牢立即停用所有ExtendWith(MockitoExtension.class)全面切换到SpringBootTest为PaymentCallbackHandler单独建TestConfiguration用Bean Primary提供Mock的PaymentService让AI学习识别“Spring Bean注入模式”扫描Autowired、Resource、getBean()调用自动选择SpringBootTest或ContextConfiguration在CI中增加spring-context健康检查mvn test -DtestSpringContextHealthCheck确保容器能正常启动。这个坑是灵魂拷问当你追求覆盖率数字时你是在测试代码还是在测试你的测试方法本身82.3%的覆盖率如果建立在脱离真实运行环境的沙盒里它就是一张废纸。6. 经验沉淀给正在路上的你的六条硬核建议这场历时两个半月的“AI补测试手术”最终交付的不只是117个测试类和82.3%的覆盖率数字更是一套可复制、可演进、可传承的方法论。作为全程操刀者我想把最锋利的六把刀交到你手上——它们不是泛泛而谈的原则而是我在键盘上敲出每一行代码、在CI日志里逐行排查时用血汗凝结的硬核建议。6.1 建议一永远先问“这个类为什么值得测试”再问“怎么测试”AI会毫不犹豫地为StringUtils.isEmpty()生成12个测试用例因为它“看起来复杂”。但你要立刻打断这个工具类已被Apache Commons Lang 3.12覆盖且线上零故障它的测试ROI投资回报率趋近于零。把有限的AI算力和人力聚焦在“业务核心域”订单状态机、优惠券核销引擎、学情同步调度器。我的判断标准就一条过去半年线上告警日志里这个类的名字是否出现过3次以上如果答案是否定的哪怕覆盖率只有5%也暂缓。把AI资源留给OrderStatusTransitionService而不是JsonUtil。6.2 建议二把“覆盖率目标”拆解为“业务规则清单”让AI照单施工别对AI说“把覆盖率提到80%”这等于让它在黑暗中摸索。你要给它一张清晰的作战地图“CourseEnrollmentManager必须覆盖1同一用户同一天选同一门课的幂等性2课程名额满时的拒绝逻辑3选课成功后学情数据的异步更新。”“RefundProcessor必须覆盖1退款金额超过实付金额的拦截2部分退款时订单状态的正确流转3退款回调失败后的重试机制。”AI的任务就是为每条规则生成1-3个精准测试。这张清单就是你的《测试契约白皮书》它比任何覆盖率数字都更能定义质量。6.3 建议三接受“不完美”但坚守“可维护”底线AI生成的第一个OrderServiceTest有47行包含5个Mock、3个verify()、2个assertThat()。我把它删了重写为Test void shouldRefundAndUpdateStatusWhenValidRequest() { // given Order order createPaidOrder(); RefundRequest request RefundRequest.builder().amount(new BigDecimal(50.00)).build(); // when RefundResult result service.refund(order.getId(), request); // then assertThat(result.isSuccess()).isTrue(); assertThat(order.getStatus()).isEqualTo(ORDER_REFUNDED); verify(paymentService).refund(eq(order.getId()), any()); }核心原则一个测试方法只验证一个业务规则一个verify()只校验一个关键协作所有对象创建用工厂方法封装。AI可以帮你生成但你必须亲手修剪——砍掉所有“看起来很酷”但无助于理解的代码。可维护性是测试资产的氧气。6.4 建议四把PITest当成你的“首席质量官”每天和它对话别只在PR时跑PITest。我在本地开发时习惯这样用