Q: 单元测试是需求"文档", 单元测试又是设计"文档", 它怎么能既是需求又是设计呢?
A: 名字和断言描述需求, 环境设置描述设计 ...
Q: 既然单元测试描述的是需求, 它就应该是黑盒测试了? 可单元测试不一直都被认为是白盒测试吗?
A: 黑白都是相对于你观察的层次. 相对于其它从外部观察"系统"行为, 不涉及源代码的测试来说, 单元测试深入到内部观察盒子的行为, 所以是白盒. 而具体到每个单元测试用例, 依然在尽可能的从外部观察"单元"的行为, 所以又是黑盒.
Q: 但是你们常用的 Mock 技术, 明显把单元测试推向白盒的境地.
A: 说来话长, 但可以先说结论: 基于状态的测试 over 基于交互/行为的测试, 虽然右边的也有巨大的价值, 但我们认为左边的更稳定和更富有对系统的洞察力
基于状态的测试描述的是需求, 基于交互行为的测试描述的是实现. 相对于需求来说, 实现更易发生变化, 尤其在另外一种实践"重构"的冲击下, 描述实现的测试将被修改的面目全非, 带来相当的返工和维护成本
一种例外, 就是交互本身就是需求, 这时 mock 是合适的选择. 一个杜撰的例子参见<<TDD: Tricky Driven Design 3, 方法>>中最后银行API的例子
而现实生活中, 存在一些情况, 虽然使用 mock 可能带来后期的维护成本, 但它带来的好处也是不可代替的. 比如对先期整体测试代码的编码量的降低. 这在 C/C++ 项目中尤其明显:
受限于C/C++的编译模型, 使用常用的预处理期接入点和编译链接接入点技术来接入 stub 实现时, 要小心维护头文件的防卫宏, 头文件的名称, 不同环境下构建脚本的include路径设置, 库路径设置等. 手工写stub的方式变的及其繁琐和容易出错. 这时候, 一个易用的 mock 框架如 mockcpp 等将节省大量的编码和先期维护工作
而几乎所有的mock框架, 都支持将 mock 对象退化为 stub, 如 mockcpp 中 mock 对象的 defaults() 设置, 或者 JMock 2 中的 Allowing . 事实上, 这是我推荐的 mock 使用方式: 通常情况下让它退化为简单的stub, 必要时才使用它强大的期待设置和验证能力.
通常单元测试有两个公认的约束需要满足:
-
快
-
隔离依赖.
重申一遍结论就是: 在满足单元测试的快和隔离依赖的前提下,
-
优先选择基于状态的黑盒测试(可使用手写stub或mock退化的stub)
-
除非交互和行为本身就是需求(可使用mock对象的全部特性)
文章来源于领测软件测试网 https://www.ltesting.net/