SpringBoot4系列21 - 测试体系JUnit 6与Testcontainers
先说一个我印象很深的场景:一家公司的订单服务上线两年,测试全靠打开浏览器手工点接口。下单、支付、查列表、删订单,一轮点下来四十分钟,改了支付逻辑还得把查询和删除再点一遍。一次完整回归半天起步,改一行代码不敢当天上线,攒到周五集中发布,发布窗口里谁也说不准哪条旧逻辑会被带崩。
先说一个我印象很深的场景:一家公司的订单服务上线两年,测试全靠打开浏览器手工点接口。下单、支付、查列表、删订单,一轮点下来四十分钟,改了支付逻辑还得把查询和删除再点一遍。一次完整回归半天起步,改一行代码不敢当天上线,攒到周五集中发布,发布窗口里谁也说不准哪条旧逻辑会被带崩。
一家公司的订单服务有三种消费方:小程序前端、运营后台、一家做对账的合作方。接口怎么调、字段什么含义,靠群里发消息同步,消息刷走一次,联调就对不上一次。我把字段说明放进共享文档试过一版,代码改了没人记得改文档,三个月之后那份文档没有一行敢信。前端问我「金额是元还是分」,答案明明就在代码里,却不在任何一份我能拿得出手的文档里。
一家公司里,订单服务背后站着三个内部系统:物流系统查订单安排发货,报表系统拉数据做统计,对账系统定时核对金额。最早的接法是三方约定一个共享密码,每个系统调接口时都带上它。我接手这种系统时最怕的就是换密码:三个系统的配置、两份脚本、一个定时任务挨个改,改漏一个就出一批 401 告警。共享密码的要害在于「一台机器一个身份、一种权限」没有任何地方可以表达,全部调用方共用同一份凭据。OAuth2 的 client_credentials 授权模式解决的正是这件事:每个系统一个 client,一个 client 一组 scope,凭据过期自动换,接口侧只认 JWT 令牌本身。
运营后台的权限需求我接到过不止一次:客服查订单,管理员取消订单、翻审计。早年我偷懒,在每个接口开头手写身份判断,if (!currentUser().isAdmin()) 复制到十几个方法里,后来加一个角色要全局搜一遍 if,说实话我自己都说不清某个接口到底谁能调。身份认证与授权是「所有请求进业务之前都要过一遍」的横切关卡,这类活儿交给 Spring Security 的过滤器链正合适,我在这篇里把整套方案在 Spring Boot 4.1.1 加 Spring Security 7.1.1 上完整落了一遍。
上个月我干了一件特别没有技术含量的事:把订单服务、库存服务、售后服务三个仓库里各一份的短消息通知代码翻出来对了一遍。发送逻辑差不多,重试次数是三个魔法数字,渠道配置三个 yml 各写各的。产品说把重试从 2 次调成 3 次,我改一处配置的活儿硬是变成了改三个仓库、发三次版、回归三遍。这类代码不属于任何一个业务,属于「所有服务都要用一份配置管理起来」的公共设施,starter 就是为它准备的封装形态。
我给订单服务定过一个小目标:每个接口统计耗时、关键操作留审计、下单支付防重复提交。这三件事都不难,难在它们跟业务一点关系都没有。没有切面的写法是把它们复制进每个方法:System.currentTimeMillis() 抬头低头各一次,审计字段在返回前手动塞库,幂等靠前端按钮置灰。我见过三个月后的下场:没人说得清哪些接口漏了计时,审计表里缺哪天的行,重复提交打穿库存的客诉截图还挂在群里。这三件事的共性是它们不属于任何业务方法,属于「所有方法都要过一遍」的横切关注点,切面正是为这类代码准备的。