【单元测试】

单元测试的逻辑

有,而且很重要。单元测试虽然看起来是"每个类自己写",但其实有一套很稳定的通用逻辑。

最常见的套路就是这四步:

准备输入

准备依赖的假行为

执行被测试代码

断言结果和交互

很多地方会把它叫做 Arrange / Act / Assert,也就是:

•

Arrange:准备数据、准备 mock

•

Act:调用被测试方法

•

Assert:检查结果

拿你这个测试举例:

TencentMailClientFacade facade = mock(TencentMailClientFacade.class);

EmailNotificationSender sender = new EmailNotificationSender(facade);

这是在准备测试环境。

NotificationRequest request = new NotificationRequest();

...

when(facade.sendMail(request)).thenReturn("mail-msg-1");

这是在准备输入和依赖行为。

NotificationResponse response = sender.send(request);

这是执行被测方法。

verify(facade).sendMail(request);

assertEquals(...);

这是断言。

除了这个总套路,单元测试还有几个通用原则:

  1. 只测一个明确行为 不要一个测试里既测成功、又测失败、又测边界。
    一个测试最好只回答一个问题。
    比如:
    •
    shouldReturnSentResponseWhenMailFacadeSucceeds
    •
    shouldGenerateMessageIdWhenFacadeReturnsNull
    •
    shouldPropagateExceptionWhenFacadeFails
    这样失败时你一眼就知道哪类行为坏了。
  2. 依赖要可控 单元测试里,数据库、Redis、MQ、HTTP、SMTP 这类外部依赖一般都 mock。
    不然测试会:
    •
    慢
    •
    不稳定
    •
    受环境影响
    •
    很难定位问题
  3. 断言两类东西 单元测试通常断言两种内容:
    •
    状态结果
    ◦
    返回值对不对
    ◦
    字段对不对
    ◦
    抛没抛异常
    •
    交互行为
    ◦
    有没有调用依赖
    ◦
    调用了几次
    ◦
    参数是不是对的
    你现在这个测试两种都断言了:
    •
    assertEquals(...) 是状态断言
    •
    verify(facade)... 是交互断言
  4. 覆盖正常路径、异常路径、边界路径 一个成熟的单元测试通常不会只测 happy path。
    一般会补三类:
    •
    正常路径
    ◦
    依赖成功,返回正常结果
    •
    异常路径
    ◦
    依赖抛异常时,你的代码怎么处理
    •
    边界路径
    ◦
    空值、默认值、特殊值、超长值等
    比如你这个 EmailNotificationSender,更完整一点至少会有:
    •
    发送成功时返回正确响应
    •
    facade 返回 null 时自动生成 messageId
    •
    facade 抛异常时异常向上抛出
  5. 测"自己的逻辑",不要测框架 单元测试的重点不是测 Spring、Mockito、JDK,而是测你自己写的逻辑。
    比如:
    •
    Spring 能不能注入 bean,这不是单元测试重点
    •
    SMTP 能不能连通,这也不是单元测试重点
    •
    你对结果的封装逻辑对不对,这才是重点
  6. 可重复、可快速执行 好的单元测试应该:
    •
    本地随时能跑
    •
    多次运行结果一致
    •
    不依赖外网
    •
    几秒内结束
    这也是为什么 mock 很常见。
    所以你可以把"单元测试的通用逻辑"总结成一句模板:
    给定一组输入和一组可控依赖,
    执行被测方法,
    断言返回结果、异常和依赖交互是否符合预期。
    如果再进一步落到你项目里,很多 service / facade / util 类的单元测试都可以套这个模版:
    // Arrange
    准备 mock
    准备输入
    预设依赖返回

// Act

调用被测方法

// Assert

断言返回值

断言异常

断言依赖调用

如果你愿意,我可以下一步直接拿你这个通知模块,给你列一个"单元测试清单",告诉你:

•

哪些类适合写单元测试

•

每个类至少该覆盖哪几种 case

•

哪些不该写成单元测试而应该写成集成测试

相关推荐
xiaoye-duck7 小时前
《C++ 工具库实战》GTest 单元测试:断言、全局环境、局部夹具与底层原理
c++·单元测试·第三方库
小凯在掘金1 天前
测试规范:为什么优先用 data-testid,而不是 CSS 选择器?
单元测试·测试
2501_915106322 天前
iOS 单元测试怎么跑?用 KXApp 在 VS Code 里跑 XCTest 和 Swift Testing
ide·vscode·ios·单元测试·个人开发·swift·敏捷流程
小莫4614 天前
把「测试有效性」加进 CI:GitHub Actions 配置指南
单元测试
汤姆宝库4 天前
推理时计算深拆:思维链、验证搜索与测试时Scaling
log4j
全栈技术负责人4 天前
AI 自动化单元测试技术方案
运维·单元测试·自动化
川石课堂软件测试7 天前
涨薪技术|0到1学会性能测试第9课-HTTP响应统计
数据库·功能测试·网络协议·http·kubernetes·单元测试·测试用例
我命由我123458 天前
SQL 检测脚本
java·数据库·sql·功能测试·单元测试·压力测试·可用性测试
神一样的老师11 天前
WS63 嵌入式 SSD1306 OLED 驱动单元测试实战
单元测试·log4j
CesareCheung11 天前
高级软件测试工程师面试锦集
面试·单元测试·压力测试