单元测试 vs 手工测试:以水印功能为例

一、一句话区分

单元测试 手工测试
谁来跑 机器(JUnit/Mockito)
测什么 代码逻辑对不对 用户体验对不对
多快 秒级 分钟~小时级
能否进 CI ✅ 每次提交自动跑 ❌ 需要人手操作

二、水印功能的测试全景

我们的水印模块涉及三个层面:路径解析 → 图片绘制 → 权限分流。项目里为每个层面写了对应的单元测试,共 3 个测试类、12 个用例,全部是纯内存测试,不连 MySQL、Redis、S3。

复制代码
项目结构
src/test/java/.../upload/watermark/
├── S3KeyUtilsTest.java                  # S3 路径解析(6 个用例)
├── ImageWatermarkServiceTest.java       # 水印绘制(2 个用例)
└── ListingPhotoAccessServiceImplTest.java # 权限分流(4 个用例)

三、单元测试长什么样

3.1 S3KeyUtilsTest --- 路径映射

测什么: URL ↔ S3 key 之间的转换规则,纯工具类,无任何 Mock。

java 复制代码
// 示例:验证新上传路径的水印 key 映射
@Test
void resolveWatermarkedKey_forNewUploadLayout() {
    // listings/original/abc.jpg → listings/watermarked/abc.jpg
    String result = S3KeyUtils.resolveWatermarkedKey("listings/original/abc.jpg");
    assertEquals("listings/watermarked/abc.jpg", result);
}

特点:

  • 输入明确,输出可预测
  • 不依赖外部系统
  • 跑完 6 个用例不到 1 秒

3.2 ImageWatermarkServiceTest --- 水印绘制

测什么: Java2D 在内存中画水印,不访问 S3、不访问网络。

java 复制代码
@Test
void applyWatermark_keepsImageDimensions() {
    BufferedImage source = new BufferedImage(800, 600, TYPE_INT_RGB);
    BufferedImage result = service.applyWatermark(source, context);

    assertEquals(800, result.getWidth());   // 尺寸不变
    assertEquals(600, result.getHeight());
}

@Test
void applyWatermark_outputsJpegBytes() {
    byte[] pngBytes = encodePng(source);
    byte[] watermarked = service.applyWatermark(pngBytes, context);

    assertNotNull(watermarked);
    assertTrue(watermarked.length > 0);
    // 验证输出是合法 JPEG
    BufferedImage decoded = ImageIO.read(new ByteArrayInputStream(watermarked));
    assertNotNull(decoded);
}

特点:

  • 手动 new ImageWatermarkService(...),不走 Spring 容器
  • 用空白 BufferedImage 当输入,classpath 下的 PNG 当 Logo
  • 只验证"能生成合法 JPEG",不验证 Logo 位置、文字内容------那些需要人眼确认

3.3 ListingPhotoAccessServiceImplTest --- 权限分流

测什么: 根据用户身份决定返回原图还是水印 URL。

java 复制代码
@Test
void resolvePhotoUrls_ownerGetsOriginal() {
    // 用户 100 看自己的房源 → 返回原图 URL
    List<String> urls = service.resolvePhotoUrls(listingPhotos, 100L);
    assertTrue(urls.get(0).contains("original"));
}

@Test
void resolvePhotoUrls_otherUserGetsWatermarked() {
    // 用户 200 看别人的房源 → 返回水印 URL
    List<String> urls = service.resolvePhotoUrls(listingPhotos, 200L);
    verify(watermarkService).ensureWatermarked(any());
}

特点:

  • 用 Mockito @Mock 模拟依赖,不启动 Spring Boot
  • 覆盖四种场景:业主、他人、助手、分享链接
  • 验证的是业务规则,不关心水印本身长什么样

四、手工测试测什么

单元测试覆盖不到的东西,就需要人来做:

测试项 为什么单元测试做不了
S3 真实上传/下载 依赖 AWS 网络,Mock 无法替代真实网络延迟和权限
Controller HTTP 接口 需要完整请求链路:Filter → Interceptor → Controller → Service → S3
水印视觉效果 Logo 位置对不对?平铺文字是否清晰?"Listed by" 条颜色对不对?这些只能靠人眼

手工测试典型流程:

  1. 启动应用,打开 Swagger UI 或 Postman
  2. 调上传接口传一张真实房源图片
  3. 调下载接口,分别用业主身份和访客身份
  4. 打开下载的 jpg,肉眼确认:
    • ✅ Logo 在右下角
    • ✅ 半透明平铺文字覆盖全图
    • ✅ "Listed by xxx" 底部条清晰可读
    • ✅ 水印图尺寸和原图一致

五、核心区别对照表

维度 单元测试 手工测试
执行速度 秒级(12 个用例 < 3s) 分钟~小时级
可重复性 完全相同输入 → 完全相同结果 人可能漏看、误判
依赖 不连 MySQL/Redis/S3 需要完整运行环境
CI 集成 ✅ 每次 push 自动跑 ❌ 无法自动化
反馈时机 写代码时立刻知道对不对 部署后才能验证
测什么 逻辑正确性 视觉体验、真实链路
维护成本 代码即文档,改逻辑时同步改 需要写测试用例文档
发现问题类型 边界条件、回归 bug 视觉瑕疵、交互问题

六、一个形象的类比

复制代码
单元测试 = 工厂质检机器
手工测试 = 质检员肉眼验收

机器能测:
  ✅ 螺丝扭矩是否达标
  ✅ 零件尺寸是否在公差范围内
  ✅ 电路是否导通

质检员看:
  👀 漆面有没有划痕
  👀 屏幕颜色是否偏色
  👀 整体手感是否舒适

两种方式互补,缺一不可。

七、项目当前测试分层总结

复制代码
┌─────────────────────────────────────────┐
│           手工测试 / 视觉验收             │  ← 人眼:Logo位置、文字、S3真实链路
├─────────────────────────────────────────┤
│        集成测试(未实现)                  │  ← @SpringBootTest + 真实 S3/DB
├─────────────────────────────────────────┤
│   Controller 层测试(未实现)             │  ← @WebMvcTest + MockMvc
├─────────────────────────────────────────┤
│   Service 层单元测试 ✅ 已实现(3类12例)  │  ← Mockito,纯内存
├─────────────────────────────────────────┤
│   Util 工具类测试 ✅ 已实现               │  ← 纯 JUnit,无依赖
└─────────────────────────────────────────┘

八、总结

  1. 单元测试验证代码逻辑------快、可重复、能进 CI,适合覆盖边界条件和回归场景。
  2. 手工测试验证用户体验------能看到真实效果,发现代码无法自证的视觉问题。
  3. 二者不是替代关系,而是互补关系。 单元测试保证"没写错",手工测试保证"没做错"。
  4. 当前项目的水印单元测试覆盖了 路径解析、绘制基础、权限分流 三个核心逻辑层。S3 读写、HTTP 接口、视觉效果仍需手工或后续集成测试覆盖。
相关推荐
HLAIA光子1 天前
AI Coding框架,打好TDD和SDD这两拳
单元测试·ai编程·代码规范
霸道流氓气质1 天前
Java 单元测试生成大量 Excel 测试数据实战指南
java·单元测试·excel
川石课堂软件测试1 天前
UI自动化测试|下拉选择框&弹出框&滚动条操作实践
开发语言·python·jmeter·ui·docker·单元测试·harmonyos
川石课堂软件测试2 天前
UI自动化测试|元素操作&浏览器操作实践
功能测试·测试工具·mysql·ui·docker·容器·单元测试
无聊的老谢2 天前
电信系统中的单元测试策略:构建高可靠性的微服务防线
数据库·微服务·单元测试
wh_xia_jun2 天前
单元测试 + Mockito 开发指南
oracle·单元测试·log4j
测试员周周3 天前
【AI测试智能体-面试】AI测试面试60题(附回答思路)
人工智能·python·功能测试·测试工具·单元测试·自动化·测试用例
摇滚侠3 天前
Spring 零基础入门到进阶 单元测试 JUnit 52-60
spring·junit·单元测试
AI thought3 天前
C语言企业项目实战(四)
c语言·单元测试·压力测试·企业项目·工程体系