测试驱动的嵌入式C语言开发(TDD)(第4-7章)
不兼容的头文件,不同的标记、函数名、定义和头文件路径,比如sprintf()和_snprintf(),解决平台独立的问题方法是适配器模式,即用C实现对不同服务的接口。CI 服务器会监控代码库的签入并在签入完成后触发一个完整的构建和测试过程。持续集成(CI),要写两套代码,代码合并要比较小,辅以自动化测试(由TDD产生),给遗留代码(没有测试的代码)建议的策略是一边产出新的产品功能,一边增量地添加。
第 4章 一路测试到完成
-
简单的想法所产生的实现会以增量的方式一点点变得更健壮
-
每个测试都把实现向完成靠拢
-
在辅助函数集成和测试通过后,需要用符号常量代替“魔数”
-
测试失败之后才添加需要的产品代码
第 5章 嵌入式系统TDD策略
脱离目标硬件测试可以触发难以出现的错误情况
-
目标硬件的瓶颈
- 硬件及其编译器比较贵、构建时间长、硬件本身有bug等
- 可以用评估板
-
双目标开发
- 代码至少要在两个平台上运行:最终的目标硬件和你的开发系统
- 硬件问题:板子的问题、CPU 的电路问题、连接及电缆问题
- 应用程序中应该只有尽可能少的部分和硬件交互
- 好处是可以容易的移植到其它平台
-
双目标测试的风险
- 开发环境与目标环境不一样
- 编译器语言特性
- 目标编译器和本地编译器的bug不一样
- 运行库不一样
- 头文件和功能不一样
- 基本数据类型大小不同
- 字节序和数据结构对齐不同
- 开发环境与目标环境不一样
-
嵌入式的 TDD 循环
- TDD 微循环是嵌入式 TDD 循环的第一个平台,如下图大部分测试开始于平台5
-
关于测试开发周期
- 平台 1:TDD 微循环,几分钟一次,运行在本地系统
- 平台 2:编译器兼容性检查,注意交叉编译器和工具链,可以作为CI/CD的一部分,调用新头文件或者库函数时自动执行
- 平台 3:在评估板上运行单元测试,可以看到代码在开发系统和目标处理器上行为的差异
- 平台 4:在目标硬件上运行单元测试,新增挑战是目标硬件上有限的内存,当测试程序不能完全放进去的时候要将测试组织成不同的单个套件,会增加自动化构建复杂度
- 平台 5:在目标硬件上运行验收测试,保证产品特性,不能自动化测试的要手动测试
-
双目标的不兼容性
-
运行时库也有 bug,目标编译器的标准库会有bug
-
持续集成(CI),要写两套代码,代码合并要比较小,辅以自动化测试(由TDD产生),
-
CI 服务器会监控代码库的签入并在签入完成后触发一个完整的构建和测试过程。如果构建失
-
败或者任何测试错误发生,团队通常会收到邮件通知
-
不兼容的头文件,不同的标记、函数名、定义和头文件路径,比如sprintf()和_snprintf(),解决平台独立的问题方法是适配器模式,即用C实现对不同服务的接口
-
-
和硬件一起测试
- 当硬件改动时,测试会帮助看到硬件新的问题
- 在虚拟环境下开发的控制硬件的代码要紧挨着硬件控制相关
- 利用外部设备进行测试
第 6章 是的,但是
-
我们没那个时间
- 测试代码的行数比产品代码多
- 手动测试,不持续,适合用于遗留代码
- 写一个测试 main()函数,或者把单元测试过程记在文档里,或者用单步跟踪都成本大,收益小
-
为什么不在写了代码之后再写测试
- TAD(后测试开发,Test-After Development)
- TDD 会影响设计
- TDD 避免缺陷发生
- TDD 中根本原因很容易找到
- TDD 更有活力,它提供更好的测试覆盖率
- TAD(后测试开发,Test-After Development)
-
测试也需要维护
-
单元测试不能发现所有的 bug
- TDD 也许不能避免所有的 bug,但它却可以非常有效地避免错误变成 bug
-
我们的构建时间太长
- 测试模块或一组模块一起测试,真正的挑战在于要有模块化的代码
-
我们有现存的代码
-
给遗留代码(没有测试的代码)建议的策略是一边产出新的产品功能,一边增量地添加
-
测试
-
-
我们的内存有约束
- 双目标、小框架、用实验板、拆解测试程序、监视内存使用情况
-
我们不得不和硬件交互
第 7章 测试替身
-
合作者
- 合作者(collaborator):就是在被测代码(Code Under Test,CUT)之外的一些函数、数据、模块或者设备
-
脱离依赖关系
-
依赖关系存在于与操作系统、硬件设备或者其它模块交互的代码上
-
脱离依赖在于严格使用接口、封装、数据隐藏、减少全局变量使用
-
测试替身不是对它要替代事物的完整模拟
-
测试替身为被测代码提供间接输入(返回值),或者用来捕获,也可能是检查由被测代
-
码发向测试替身的间接输出(参数)
-
-
如何使用测试替身
- 如果可以就用真实代码,只有必要时才使用测试替身
- 使用替身的理由
- 独立于硬件
- 注入难以产生的输入
- 加速缓慢的合作者
- 依赖于不稳定的事物
- 对在开发的事物的依赖
- 对于难以配置的事物的依赖
-
用C来仿冒,下一步
- 不同的测试替身,不用在意,可以笼统的将仿冒、仿制对象和桩来表示同一个东西
- 测试哑元(test dummy),从来不被调用
- 测试桩(test stub),在当前的测试用例的指示下返回某些值
- 测试间谍(test spy),捕获并校验被测代码传出的参数,也可提供返回值
- 仿制对象(mock object),校验函数调用、调用顺序,从被测试代码传向 DOC 的参数
- 仿冒对象(foke object),为其替代的组件提供一个部分的实现,更简单
- 引爆仿冒(exploding fake),当调用到其时引发测试失败
- 链接时代换:为整个单元测试可执行文件来替换 DOC 时使用链接时代换
- 函数指针代换:只为一部分测试用例替换 DOC 的时候可以使用函数指针
- 预编译代换:在链接器和函数指针都不能工作时使用预编译代换,这是最好的选择,比如使用预编译代换来重载标准库函数 free()、malloc()、calloc()以及 realloc()函数
- 组合链接时和函数指针代换:可以合并链接时和函数指针代换来让它们一起工作
- 不同的测试替身,不用在意,可以笼统的将仿冒、仿制对象和桩来表示同一个东西
更多推荐
所有评论(0)