Typst 简历模板 modern-cv 视觉回归测试实战:typst-test 测试体系完整解析
Typst 简历模板 modern-cv 视觉回归测试实战:typst-test 测试体系完整解析
modern-cv 是一个用 Typst 编写的现代简历模板(受 Awesome-CV 启发),支持简历和求职信排版、多语言、主题色定制等。但"排版类项目"最容易踩的坑恰恰是:改一行样式,简历的像素位置就悄悄变了。为此,项目团队搭建了一套基于 typst-test 生态的视觉回归测试体系,用"截图对比"守住每一次改动的输出质量。本文带你完整解析这套测试的设计思路、目录结构与运行方式,5 分钟即可上手实战。
一、为什么简历模板需要"视觉回归测试"?
传统代码测试验证的是"逻辑对不对",而 Typst 简历模板的核心产出是版式本身:
- 标题字重调了 1pt,会不会把"工作经历"区块挤到第二页?
- 改了主题色,页脚的日期格式会不会错位?
- 换了一版字体,中文姓名顺序(姓在前)的渲染分支还对吗?
这类问题靠肉眼看"应该没问题"是不靠谱的。**视觉回归测试(Visual Regression Testing)**的做法很简单粗暴:
- 把一个"标准答案"截图存为参考图(reference);
- 每次改动后重新编译测试文档,生成新截图;
- 逐像素对比两张图,不一致就报警。
modern-cv 正是用这种方式,让简历、求职信等核心模板的渲染结果"有据可查"。
二、测试体系全景:tests 目录结构一览
所有测试用例集中在 tests/ 目录,每个子目录对应一个独立场景,目录内部遵循统一约定:
tests/
├── <用例名>/
│ ├── test.typ # 测试文档:像写简历一样写
│ └── ref/
│ └── 1.png # 参考截图(标准答案)
现有的测试用例覆盖了模板的主要功能面:
| 测试目录 | 覆盖场景 |
|---|---|
| resume-header/ | 简历头部(头像、主题色、联系方式) |
| resume-section/ | 章节条目:教育、认证、技能、GPA |
| resume-options/ | 各类配置选项的渲染 |
| resume-language-zh/ | 中文/日文姓名顺序分支 |
| coverletter/ | 求职信完整版式(签名、收件人信息) |
| coverletter-options/ | 求职信选项开关 |
| contact-icons/ | 社交图标与联系方式排版 |
| hiring-entity-info-language/ | 招聘方信息的语言本地化 |
| resume-skill-grid/ | 技能网格布局 |
| resume-entry-title-link/ | 条目标题链接 |
| utilities/ | 底层工具函数与图标 |
| date-localization/ | 日期格式本地化(纯断言测试) |
💡 可以看到测试是"按功能切片"组织的,而不是一个大而全的全量测试——每个用例只验证一件事,定位问题非常快。
以简历头部为例,它的参考图就是下面这张由测试编译生成的标准输出截图:
三、一个典型的视觉回归测试长什么样?
测试文档本身就是一个"迷你简历"。以 tests/resume-section/test.typ 为例,它的写法和你自己写简历几乎没有区别:
#import "@local/modern-cv:0.11.0": *
// 测试各章节函数
#resume-item("Education")
#resume-entry(
title: "BSc Computer Science",
location: "Example City",
date: "2019 - 2022",
description: "Achieved acaademic honors and awards.",
)
#resume-skill-item("Programming", (strong["C++"], "Python", "Java"))
#resume-gpa("3.5", "4.0")
几个值得注意的细节:
- 通过
@local前缀导入本地库:Justfile 的install配方会先把库打包安装到@local前缀,测试始终针对"当前工作区代码"而非已发布版本——这正是回归测试能捕捉本地改动的关键; - 测试文档复用模板同款排版设置(字体、页边距、标题样式),保证测试环境与实际使用环境一致;
- 注释即文档:像 tests/resume-language-zh/test.typ 会详细解释为什么用拉丁字母模拟中文姓名(CI 镜像未安装 CJK 字体),避免未来维护者踩坑。
求职信测试同样遵循这个模式,tests/coverletter/test.typ 配置了完整的作者信息、签名图片与招聘方信息:
四、一键运行测试:just 命令速查
项目用 Justfile 把常用操作做成了配方,无需记忆冗长命令:
| 命令 | 作用 | 底层实现 |
|---|---|---|
just install |
将本地库打包安装到 @local 前缀 |
scripts/package |
just test |
先 install,再运行全部测试 | tt run --use-system-fonts --no-fail-fast --warnings promote |
just update |
测试改动无误后,更新参考图 | tt update --use-system-fonts |
just ci |
跑测试 + 生成文档手册 | test + doc |
对应的工具是 typst-test 生态中的 tytanic(命令名为 tt),工作流非常清晰:
- 改代码 →
just test:所有用例与ref/下的参考图对比,有差异则失败并生成差异图; - 确认改动是预期内的 →
just update:重新生成参考图,提交即可。
⚠️ 注意:参考图是测试的"标准答案",更新前务必确认差异是有意为之,否则会把 bug 固化进基线。
工具版本通过 mise.toml 统一锁定(typst 0.14.2、tytanic 0.4.1 等),保证团队成员和 CI 环境的渲染结果一致——对像素级对比来说,这一点至关重要。
五、不只比像素:断言式单元测试
视觉对比适合"看版式",但像日期格式化这类纯逻辑功能,用断言测试更精准。tests/date-localization/test.typ 就是典型的例子:
#let d = datetime(year: 2026, month: 8, day: 28)
#assert.eq(__format_date(d, "en"), "August 28, 2026")
#assert.eq(__format_date(d, "de"), "28.08.2026")
#assert.eq(__format_date(d, "zh"), "2026年08月28日")
#assert.eq(__format_date(d, "xx"), "August 28, 2026") // 未知语言回退 en
它直接导入库内部函数 __format_date,逐语言断言输出格式,连"未识别语言回退英文""自定义字符串原样透传"这类边界行为都覆盖到了。文件头部的注释还注明了这是针对真实 Bug 报告的回归测试——每个测试用例都对应一个可追溯的问题,这就是回归测试最有价值的地方。
六、测试如何守护版本发布
这套测试并非摆设,而是发布流程的硬性关卡。docs/release-checklist.md 列出了发版步骤,其中明确要求:
- Ensure that tests pass.(确保测试通过)
- Ensure that the documentation manual builds.(确保文档手册可构建)
也就是说,视觉回归测试不通过,就不会有下一个版本。从改一行样式到发版,质量保障链条是完整的。
七、快速上手:5 分钟跑通完整测试
# 1. 克隆仓库
git clone https://gitcode.com/gh_mirrors/mo/modern-cv
cd modern-cv
# 2. 安装并运行测试(tt 命令来自 tytanic)
just test
# 3. 本地有意改动模板后,确认渲染无误再更新参考图
just update
如果你的机器尚未安装 tt(tytanic),参考 mise.toml 中的版本声明 0.4.1 安装同版本即可;字体需求(Roboto、Source Sans 3、FontAwesome)可参考 README.md 中的说明,项目还贴心地提供了 scripts/install-roboto、scripts/install-fontawesome 等安装脚本。
总结
modern-cv 的测试体系可以概括为三句话:
- 按功能切片:
tests/下 12 个用例各守一方,覆盖简历、求职信、本地化、工具函数; - 截图即基线:typst-test 生态的
tt run/tt update完成"对比—更新"闭环,just一行命令搞定; - 双轨验证:视觉回归管版式,
#assert.eq断言管逻辑,二者互补。
这套"小而美"的测试架构对任何做排版、做主题的 Typst 项目都是很好的参考——版式产品的回归测试,本质上就是给"好看"建立可复现的量化标准。


