第六章:测试、调试与性能监控

目标 :保证 Markdown 模块 正确、快速、稳定
范围

  1. 单元 / UI / 快照 / 端到端测试
  2. 渲染埋点与性能监控

CI/CD 与灰度 属于 低优先可选 。如需集成,请参考本章末尾的 附录 A


6.1 测试金字塔

层次 覆盖内容 工具 / 框架
单元 (Unit) Markdown 解析、工具函数 JUnit5 · Truth
UI (Widget) View 属性、交互 Espresso · Robolectric
快照 (Snapshot) 像素级一致性 Paparazzi · Shot
端到端 (E2E) 实机网络、路由 AndroidX Test · MockWebServer

6.2 单元测试示例

kotlin 复制代码
@Test
fun heading_shouldParseAsHeadingNode() {
    val node = Parser.builder().build().parse("# Title")
    assertThat(node.firstChild).isInstanceOf(org.commonmark.node.Heading::class.java)
}
  • 每条 CommonMark 语法 + 自定义插件各写 1 成功 + 1 失败用例。
  • 渲染前后 RenderProps 行为也需断言。

6.3 UI & 快照测试

6.3.1 Espresso

kotlin 复制代码
@Test
fun notePanel_toggleBody() {
    launchActivity<DemoActivity>().use {
        onView(withText("NOTE")).perform(click())
        onView(withId(R.id.body)).check(matches(isDisplayed()))
    }
}

6.3.2 Paparazzi 快照

kotlin 复制代码
@Test
fun codeBlock_snapshot() {
    paparazzi.snapshot {
        CopyableCodeBlockView(context).apply {
            render(context, SAMPLE_CODE, defaultParams, null)
        }
    }
}
  • 快照 diff > 1 % 视为渲染回归,CI 自动 fail。

6.4 渲染埋点与监控

6.4.1 关键指标

指标 目标 (p95) 说明
Parse_TT (ms) ≤ 50 Parser.parse()
Render_TT (ms) ≤ 90 Markwon.setMarkdown()
FCP (ms) ≤ 500 Activity 首帧
Jank_% ≤ 5 % 滚动掉帧率
Image_Fail (%) ≤ 1 % 图片加载失败率

6.4.2 Markwon 插件埋点

kotlin 复制代码
class MetricsPlugin(private val log: (String, Long) -> Unit) : AbstractMarkwonPlugin() {
    private var t0 = 0L; private var tParse = 0L
    override fun beforeSetText(tv: TextView, md: String) { t0 = now() }
    override fun configureParser(b: Parser.Builder) {
        b.postProcessor { node -> log("Parse_TT", now() - t0); node }
    }
    override fun afterRender(node: Node) { tParse = now() }
    override fun afterSetText(tv: TextView) { log("Render_TT", now() - tParse) }
    private fun now() = SystemClock.elapsedRealtimeNanos()
}

6.4.3 帧率采集

kotlin 复制代码
val agg = FrameMetricsAggregator.EVERY_DURATION
agg.add(activity.window)
// onStop
val slow = agg.frameMetrics[0]?.get(FrameMetrics.TOTAL_DURATION) ?: 0

6.4.4 图片监控 (Glide)

kotlin 复制代码
glide.load(url)
    .listener(object : RequestListener<Drawable> {
        override fun onLoadFailed(e: GlideException?, m: Any?, t: Target<Drawable>?, p: Boolean): Boolean {
            Metrics.log("Image_Fail", 1); return false
        }
        override fun onResourceReady(r: Drawable?, m: Any?, t: Target<Drawable>?, d: DataSource?, f: Boolean): Boolean {
            Metrics.log("Image_OK", 1); return false
        }
    }).into(iv)

6.4.5 数据上报

proto 复制代码
message MdMetric {
  enum Type { PARSE_TT = 0; RENDER_TT = 1; FCP = 2; JANK = 3; IMAGE_FAIL = 4; }
  required Type type   = 1;
  required int64 value = 2; // ms 或 次数
  optional string ver  = 3; // App 版本
}
  • 优先本地批量 + gzip,再 HTTPS 上报。
  • Grafana 面板:Parse TT、Render TT、Jank_% 随版本折线。

6.5 快照 & 差异治理流程

  1. PR 中的 GitHub Action 运行
    ./gradlew verifyPaparazziDebug
  2. 差异>1 % → Action fail,输出 diff PNG
  3. 开发核对:
    • UI 设计变更 → 更新快照生成新的 baseline
    • 非预期 → 修正代码,再跑一次 CI

6.6 故障排查指南

现象 快速定位 可能根因 解决
首帧 > 1 s Trace 查看 Parser.parse 大文档同步解析 后台线程 + LRU 缓存
滚动掉帧 FrameMetrics 看 Draw > 16 ms 复杂表格反复 measure View 复用 / 分页
图片空白 Image_Fail% 升高 URL 失效 / SSL 占位图 + 重试
内存涨 LeakCanary Trace Span 持有 Context 使用 applicationContext

6.7 章节 Checklist

阶段 动作
测试 单元 + UI + 快照 覆盖率>80 %
埋点 Parse/Render/FCP/Jank/ImageFail
监控 Grafana 仪表板实时可见
故障 快照 diff + LeakCanary 自动告警

附录 A(可选):CI/CD 与灰度发布

优先级低------可在基础功能稳定后按需接入

  1. GitHub Actions / GitLab CI
    • 步骤:依赖缓存 → 单测 & 快照 → Lint → Assemble → 上传 APK
  2. Gradle 多 flavor
    • canary / beta / prod 打包脚本
    • BuildConfig.BOOL_PLUGIN_ENABLED 控制插件灰度
  3. 远程开关
    • Firebase Remote Config / 自建 CDN JSON
    • 指标监控低于阈值可热关新插件
  4. fastlane
    • 一键推送到内测 & 生产
    • 自动回滚脚本:CrashFree < 99 % → 压回旧版本

若需完整 YAML / fastlane 脚本,可参考上一版本内容或内部 DevOps 模板。


小结

  • 测试金字塔 + 快照------锁死回归
  • 埋点监控------发现性能衰退
  • CI/CD------可选加速手段,后期接入
  • 灰度------版本健康,数据驱动开关新能力
相关推荐
海绵宝宝de派小星6 小时前
模型规模与涌现能力(Emergent Abilities)
ai
AlfredZhao6 小时前
RAG 时代的“破壁人”:为什么你的大模型应用急需 Docling?
ai·rag·docling
Elastic 中国社区官方博客7 小时前
如何防御你的 RAG 系统免受上下文投毒攻击
大数据·运维·人工智能·elasticsearch·搜索引擎·ai·全文检索
SQL必知必会8 小时前
SQL 窗口帧:ROWS vs RANGE 深度解析
数据库·sql·性能优化
Elastic 中国社区官方博客9 小时前
Elasticsearch:交易搜索 - AI Agent builder
大数据·人工智能·elasticsearch·搜索引擎·ai·全文检索
yuanmenghao12 小时前
Linux 性能实战 | 第 15 篇 磁盘 IO 性能分析与瓶颈定位 [特殊字符]
linux·python·性能优化
消失的旧时光-194313 小时前
第十八课:后端性能优化方法论——从 SQL 到 JVM 到接口(工程实战全景版)
性能优化
蚂蚁开源13 小时前
AReaL 团队开源 ASearcher 项目,解锁搜索智能体领域的最新突破
ai·开源
泓博14 小时前
OpenClaw自主写文章并发送到公众号
ai