最佳实践 | 单元测试 + 回归测试在 SRS 代码提交中的实践总结
为了解决 SRS WebRTC 推流, 转 RTMP 后音视频时间戳不同步, 导致的后续 HLS 切片,FLV/RTMP 播放音画不同步等问题,我提交了一个 PR:https://github.com/ossrs/srs/pull/2470
其实就是依赖 SenderReport 来同步 RTP 时间戳和绝对时间戳。做完了以后,简单的跑了下, 发现输出符合预期, 就满心欢喜的提交了 PR, 等待合并。
最先 review 代码的是 SRS 技术委员会的进学, 他提出了一个问题:“如果 Sender Report 乱序了,计算出来的时间戳是对的吗?”因为我测试的时候偶现过这种场景, 我信心满满的回复, ”没问题, 我测过“, 一番讨论后, review 就通过了。
这时候成立冷不丁来了一句:“能用单元测试覆盖吗?”虽然知道单元测试的重要性, 但因为懒惰, 没有尝到甜头等原因, 我一直都不愿意去多做单元测试, 总觉得差不多就得了。但是在当时的场景下, 催生了我的好胜心,就像当年大师兄和我说的差不多, 得把代码搞出来才算。
一不做二不休, 我就开始着手写单元测试, 顺便再补充个回归测试, 干活就要干全套, 不然就白干了!大概花了几天的时候系统的学习了 GTEST 和 GMOCK 以后, 我就把单元测试写完了, 我心想这事情也没有想象中的难嘛,完全是个脏活累活, 不就是构造一些参数, 逐个函数验证嘛。
跑起来以后, 屏幕上出现了大大的红字“TEST FAILED”,当初提交 PR 时候的自信立马就被现实啪啪打脸。
重复跑了几次, 发现错误又没了, 意识到这是一个偶现的 BUG, 打上日志,反复跑了几次后, 捕捉到了想要的错误。对照着单元测试的错误,我修复了几个非常细节的代码错误。具体如下:
补上一个 NTP 的单元测试, 确认了之前的问题:
通过 demo 程序进行验证:
打开 IETF 浮点数在线解释的页面
https://www.h-schmidt.net/FloatConverter/IEEE754.html 进行分析:
到这里基本就知道怎么改了, 加一个 round 函数, 四舍五入, 问题迎刃而解。
改完重新跑测试, 通过!
经过这一次实践, 单元测试给我带来的体会是:
以后再怎么改代码, 跑一遍单元测试, 起码能让人安心, 知道这一次修改的代码没有基本的函数级别的错误;
试想一下如果没有用单元测试发现这几处细微的代码错误, 合并到 4.0released 后, 最终问题还是会变成 ISSUE 回来, 而且更隐晦, 更难排查。从长远看, 单元测试节省了开发者的时间。
搞完了单元测试,接下来就是搞回归测试。
为什么需要回归测试,通俗的说, 只保证了单元的正确性, 但是多个正确的单元有可能错误的结合, 所以我们需要回归测试, 来保证业务逻辑代码的正确性。
SRS 里面是怎么做的
https://github.com/ossrs/srs/tree/4.0release/trunk/3rdparty/srs-bench/srs 这个目录下, 提供了压测工具和单元测试工具。
原理也很简单, 用 GO 自带的 TEST, 结合 GO 语言开发快的特点, 用 PION 这个库做了一些简单的回归测试,都有些啥:
DTLS 握手的丢包,乱序, 重复等多种可能的测试;
RTC 推一个自带的阿凡达视频, 验证能不能用 RTC 拉流;
其他一些基本的流程验证。
在这个基础上, 我补充了一个 RTC 推流, FLV 播放, 验证音视频时间戳是不是对齐的逻辑。截取个代码片段:
写完后,本地多次验证 OK, 安心提交。
得益于 github 完善的机制, 提交后自动跑单元测试,回滚测试,各种环境的编译脚本。全绿!通过!
有了单元测试 + 回归测试这俩牛逼的组合, 对于开发者来说, 提交代码更安心了, 虽然全部测试通过不一定意味着没问题, 因为可能有一些函数和逻辑没有被测试覆盖到, 但是有不通过的测试一定意味着有问题, 这就足够了, 保证了 BUG 尽量在早期被发现, 提升软件的可靠性。
SRS 的未来, 希望每一个 PR, 都能用测试用例覆盖。
版权声明: 本文为 InfoQ 作者【腾讯云音视频】的原创文章。
原文链接:【http://xie.infoq.cn/article/08100d8289bf807f481775fd9】。文章转载请联系作者。
评论