切尔斯基

冰河洗剑,绝塞传烽,江山如画雪初晴

用户操作
[即时聊天] [发私信] [加为好友]
切尔斯基ID:chelsea
137114次访问,排名593好友0人,关注者7
chelsea的文章
原创 38 篇
翻译 2 篇
转载 0 篇
评论 123 篇
切尔斯基的公告
质疑暂停, 发些以前的外传
最近评论
Warhammer Gold:metin2 yang
dofus kamas
比喻很恰当,呵呵.
suxiaoguai:敏捷有好处也有短板:
==================
如果结对的双方没有类似的知识结构,效率一定不会高。这种情况下双方之间更多的是思想上的摩擦和相互学习,不是合作解决实际问题。结对需要长时间的前期投入才能开始起作用。
==================
这是个问题,所以敏捷一定需要有公司支持这个土壤。
另外团队的文化非常重要,
hachenzhonghua:结伴比较难。
这个对企业整体文化、管理能力要求比较高。
心胸坦荡的人并不多。落井下石的人却总是不缺
iceheart:你跟某人适合xp跟另外一些人不见得适合,某个项目适合xp另外的项目不见得适合,某些环境适合xp另外的不见得适合。

脱离了具体情况的空谈都是没有意义的争论
文章分类
    收藏
      相册
      切尔分站
      切尔斯基的工作(RSS)
      切尔斯基的工具(RSS)
      切尔斯基的杂记(RSS)
      亲密爱人
      丸子(RSS)
      文学艺术
      芳村窝蛋(RSS)
      存档
      订阅我的博客
      XML聚合  FeedSky

      原创 敏捷质疑: TDD收藏

      新一篇: 敏捷质疑: 持续集成 | 旧一篇: 假冒的艺术

      Q: 为什么通过单元测试发现的 Bug 很少 ?

      A: 单元测试不是用来发现 Bug 的, 而是用来预防 Bug 的. 如果采用 TDD, 测试用例完成之时, 产品代码尚未编写, Bug更无从谈起.

       

      Q: 那是否写单元测试就能提高代码质量了 ?

      A: 关于这一点, 似乎有人不这么看, <<TDD Opinion: Quality Is a Function of Thought and Reflection, Not Bug Prevention>>. 不错, 代码质量并不必然关联到单元测试, 诸如净室软件开发之类的方法依然可以在没有单元测试的情况下得到高质量的代码, 但这是另外一个问题. 或许主观上, TDD的本质更接近于促使你把质量内建在思维中, 但客观上, 在其它条件都相同的情况下, 单元测试依然能够起到预防 Bug 的作用.

       

      Q: 单元测试怎么能反映/代替需求 ?

      A: 单元测试未必能直接反映宏观上的需求, 但

      1. 功能测试和集成测试能够反映宏观需求.

      2. 单元测试能够反映系统的其它部分对当前单元的需求.

      而从文本的角度, 测试用例的名字就是需求的描述. 换句话说, 你从传统的需求文档中把描述抠出来, 放到测试代码中作为测试用例的名字, 你便拥有了可执行的需求文档

      一个 RSpec 写的功能测试用例 (不要怀疑, 它确实是可以运行的):

        it "should show welcome message after login" do

          login_as_chelsea

          get :index

          response.should have_text(/欢迎 chelsea/)

        end

        it "should not show welcome message after logout" do

          logout

          get :index

          response.should_not have_text(/欢迎/)

        end

      单元测试的例子:

          public void testShouldBeFreeFrom2amTo5am() throws Exception { //直接业务需求

              ...

          }

          public void testShouldThrowExceptionIfCannotFindConfigFile() throws Exception { //来自系统其它部分的需求

              ...

          }

      测试用例并不排斥业务层面的需求文档, 一个高层的, 突出业务价值的需求/愿景描述对于快速理解系统是非常有帮助的, 但/只是测试用例以另一种方式描述了真实的系统, 它具有两个突出的优点:

      1. 它不会说谎, 即永远与系统真实的行为同步

      2. 它是可执行的, 它可以不知疲倦的, 成本极低的, 时时刻刻, 反反复复的追问你的系统是否符合需求

       

      Q: 需求变了怎么办? 岂不是有大量测试用例需要修改?

      A: 难道不是应该的吗? 难道以前的需求文档在需求发生变化时不需要修改?  哦, 或许它们不需要, 因为没人会关心, 对代码也没什么影响, 需求文档在度过最初的几周后便被扔在配置库里再也没人管它了.

      1073x: "这也是很多程序员不愿意作单元测试,并且抵制TDD的原因(虽然他们两者根本不是一回事)。在开发过程中确定什么应该用测试用例描述,什么可以不用,是很重要的。一段产品代码与之对应的测试代码的粒度,数量,都是需要斟酌的问题。"

      是的, 这关系到你的测试策略. 然而通常的测试策略对单元测试的要求都是尽可能周全. 于是这就是一个测试设计的问题. 是的,测试代码也需要设计, 也需要重构, 也需要 Domain Specific.

       

      Q: 我的单元测试编译链接速度很慢, 而且有些条件很难测, 比如内存不足, 或者环境很难搭建, 比如需要网络或数据库, 怎么解决?

      A: 这是集成测试, 不是单元测试. 你一定把系统所有的组件都编译链接起来了. 那么如果你的测试失败了, 是哪一部分的问题呢?

      通常单元测试需要满足一个条件: 不依赖任何其它单元, 即隔离性. 实现手段就是在测试环境中能够轻易的假冒依赖, 并设定依赖按照我们的意愿进行工作. 一个例子就是你的代码依赖 malloc 获取内存, 而你想测试内存不足的情况. 那么我们应在能够/需要在单元测试中使用使用一个假冒的 malloc 来代替真正的 malloc, 并且我们能控制假冒的 malloc 返回 NULL 以模拟内存不足的情况. 关于如何做到这一点, 可参考一些成熟的"假冒"框架, 如 mockcpp 等.

       

      Q: 我原来的测试都是用真实的代码来跑, 一个测试能覆盖多个单元. 你现在都把依赖替换掉了, 那被替换掉的模块有问题怎么办? 怎么保证集成真实的代码后还能正确工作?

      A: 其它单元有其它单元自己的单元测试, 各自关注自己. 集成测试像以前一样, 该怎么测还怎么测, 并不是有了单元测试就不要其它测试了.

       

      Q: 单元测试就是设计? 单元测试怎么能反映/代替设计 ?

      A: 单元测试反映的是局部的设计, 局限于本单元以及与之交互的其它单元. 前面说的单元测试能够反映系统的其它部分对当前单元的需求, 所谓设计就是单元之间的职责划分, 交互和依赖关系

      当你试图测试一个单元时, 却发现需要创建大量的其它对象, 而且按照你脑海中的实现, 有些对象是在单元内部创建的, 根本无法在测试环境中假冒它们. 这时候, 你即使只是为了减少测试的难度, 也会逼迫自己思考:

      1. 这个单元是否做了太多的事, 承担了额外的职责, 违反了单一职责原则?

      2. 是否应该把依赖让外界设置进来, 而不是自己在内部创建, 这样测试时就能把依赖设置为假冒的实现?

      是的, 单元测试警示你思考一下自己的设计

       

      Q: 单元测试是设计, 还有人说源代码是设计, 到底是测试是设计还是源代码是设计?

      A: 这实际上是另外一种角度. 源代码就是设计的论断基于两个假设

      1. 设计阶段中工程师的工作产物, 也就是他的设计, 是应该能够在实施阶段被不同的实施者严格并且几乎一模一样的实现

      2. 软件开发人员也是工程师, 即软件工程师

      如果我们认同这两个假设, 那么软件工程师的什么产物能够被严格并且重复实现的呢? 是你的Word形式的"设计"文档吗? 是CAD工具画出的UML图吗? 都不是, 因为它们都不精确, 有无数种实现方式, 根本谈不到严格, 不同的开发人员会有完全不同的实现. 事实上, 只有源代码,才能满足这个约束. 这样软件的设计阶段, 就是直到软件工程师完成源代码的那一刻, 而软件的实施阶段, 其实就只剩编译和部署了. 跑题了.

       

      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, 必要时才使用它强大的期待设置和验证能力.

      通常单元测试有两个公认的约束需要满足:

      1. 隔离依赖.

      重申一遍结论就是: 在满足单元测试的快和隔离依赖的前提下,

      1. 优先选择基于状态的黑盒测试(可使用手写stub或mock退化的stub)

      2. 除非交互和行为本身就是需求(可使用mock对象的全部特性)

       

      Q: 怎么测 private 函数?

      A: 把它变成 public 的.

      我是认真的. 如果发现 private 函数无法简单的通过某个public函数的测试来覆盖而需要专门的测试, 意味着你的单元可能承担了太多的职责, 应该拆分到一个单独的单元中, 并开放为 public 函数.

      如果使用 C++, 在测试环境中 #define private public.

      如果使用 g++, 在测试环境中加入 -fno-access-control.

       

      Q: 类似 private, 一些意图实现良好设计的语言特性, 如 static, sealed, final, 非虚函数等, 却总是给代码的易测试性带来麻烦, 该如何取舍?

      A: 没什么好办法. 这些语言特性和测试的目的是相同的, 都是为提高代码质量, 减少出错的可能, 虽殊途同归, 但却互相限制, 效果也不一样.

      我认为工业界是时候严肃认真的考虑测试环境了, 最好在语言中内建对测试的支持, 一些为产品环境设计的语言特性, 应该在测试环境中关闭, 而在产品环境中生效. 其实之前很多编译器都支持 Release 和 Debug 两种环境, 也是从代码质量的方面考虑的. 现在毫无疑问证实单元测试比 Debug 更有效, 是时候与时俱进增加对 Test 的支持而逐渐罢黜对 Debug 的支持.

      在语言本身增加对测试的支持之前, 我们不得不想办法在测试环境中绕过语言特性的限制, 尤其对遗留系统, 代码已经存在的情况. 比如对于 C++ 中的 static 函数, 可以将整个被测单元 #include, 或者 #define static 为空. 宏代表了一层间接, 在测试环境中, 这层间接是至关重要的. 其它方法可参考 <<Working Effectively with Legacy Code>>, <<假冒的艺术>>中的介绍.

       

      Q: 刚才提到了要支持"测试"而不是"Debug", 测试和Debug难道有什么矛盾吗?

      A: 有. 如果你发现不得不 Debug, 就是测试粒度太粗, 步子迈的太大, 产品代码过长等导致的, 甚至可能你卷入了过多的单元而破坏了测试的隔离性. Debug还是代码逻辑不清, 行为难以断言的表现.  用测试帮你定位错误.

       

      Q: 我知道为遗留系统增加新特性是要先写测试保证系统原来的行为, 可遗留代码很庞大, 我甚至都不知道系统目前的行为, 怎么办?

      A: 特征测试: 保持代码行为的测试, 获取当前运行结果, 来填充测试, 以获取系统目前行为. 其实测试可以分为两类: 试图说明想要实现的目标, 或者试图保持代码中既有的行为; 在特性实现后, 前者会转化为后者. 详细信息请参见<<Working Effectively with Legacy Code>>

       

      Q: 有成熟的关于在遗留系统上实践 TDD 或者单元测试的实践吗?

      A: 还是<<Working Effectively with Legacy Code>>, 或者<<在大型遗留系统基础上运作重构项目>>

       

      Q: 前面经常说到 C++ 或其它面向对象语言, 却没有提到 C, 那么过程式语言中如何应用 TDD ? 有什么不一样?

      A: 基本一样, 并且在过程式语言中应用 TDD, 可能会导出面向对象风格的设计. 比如如果直接调用某个函数, 那么不得不通过编译时替换或链接时替换来接入假的实现. 这样其实比较麻烦, 因此可能会促使你选用函数指针 ,以便方便的在测试环境中进行替换. 随着时间的推移, 你会发现一组组概念相关的函数指针出现了, 那么把它们和它们操作的数据绑定在一起, 定义一个 struct, 就形成了一种对象风格. 当然这反而可能会令你的代码更复杂, 这需要在实践中取舍.

      也有可能在过程式语言中你觉得 TDD 对设计的促进不大, 而且测试用例也比较枯燥, 就是测个分支, 返回值什么的. 是的, 逻辑就隐藏在分支和返回值中, 如果习惯了过程式思维并不打算改变, TDD 对设计的影响则更多的体现在依赖管理上, 如头文件和编译单元的职责划分. 如果把不同职责的函数混在一个编译单元里面, 则很难实施链接替换等手段, 除非你选择一个类似 mockcpp 的框架, 不需要链接替换.

       

      Q: 如果使用 TDD, 那么测试人员怎么安排? 是不是一开始就要进入项目组? 可那时还没有产品代码,测什么?

      A: 是, 是一开始就要进入项目组, 可不是因为 TDD.  是, 测试人员是一开始没什么可测的, 可不代表就没活干.

      TDD是一种开发方法, 是开发人员参与的活动. 其效果是以可执行的形式文档化你的需求, 迫使你分清职责隔离依赖以驱动你的设计, 编织安全网以扼杀Bug在摇篮状态防止逃逸. 可传统测试人员的活动是试图找到已经逃逸的Bug. 这两种活动都是必要的, 而且毫不冲突, 互为补充.

      那么测试人员在新的特性还没开发完成之前做什么呢? 除了提前写测试用例, 无论是自动化的还是非自动化的, 而需要测试人员参加的一项重要活动, 就是参与特性验收条件的制定. 之前经常发生开发人员按照自己的理解去编码, 测试人员按照自己的理解去测试, 直到开发完成, 测试过程中才发现理解的不一致, 开始产生争执并阻塞等待业务分析人员(如果幸运的话)或者行政主管(如果开发过程混乱的话)的仲裁. 解决办法就是就在开始开发新特性前的一刹那, 由业务分析人员, 测试人员, 开发人员进行一次讨论, 就验收条件达成一致并形成记录, 然后测试人员和开发人员分头去写测试和实现.

       

      Q: 之前会有一个阶段, 就是一组相关的特性开发完成后, 测试人员接手测试, 几轮Bug修复过去后, 产品基本稳定就可以发布了. 现在测试人员提前介入到每个迭代中, 针对单个特性进行测试, 那如何保证产品集成起来的质量?

      A: 跟以前一样, 该有那么个集成测试阶段还得有那么个集成测试阶段, 取决于产品当时的质量状态. 并不是说有了迭代级别, 单个特性级别的测试就不需要发布级别的集成测试了, 两者没有任何矛盾.

       

      Q: 那么测试人员提前进入迭代有什么好处?

      A: 尽早发现问题, 降低修复错误的成本. 有几种手段, 一是前面提到与业务人员和开发者一起讨论验收条件, 这样就能防止理解偏差而导致的返工. 二是开发完成立即测试, 发现问题立即反馈, 这样开发人员对代码依然印象深刻,能快速定位和修复错误. 这样流入最后集成测试阶段的Bug就会少, 会缩短最后的集成测试时间, 保证产品更平稳的发布.

       

      Q: 有时候后续的特性会影响前面的特性, 那么迭代过程中测试人员只测单个特性, 怎么保证以前的特性依然工作?

      A: 几个手段. 测试尽量自动化, 以便能够持续集成. 再就是做好依赖管理, 每当一个新特性完成, 就应该能够发现它影响的其它特性, 看看是否应该补充一些集成测试.

       

      Q: 有时候开发人员完成一个特性时已接近迭代结束, 测试人员没有时间进行充分测试, 怎么办?

      A: 下个迭代测呗, 并且在计算开发速度时, 只应该计算本迭代通过测试人员验收的特性, 那些仅仅是开发人员完成, 没有经过测试人员充分测试的特性不计在内. 这种情况是不可避免的. 但我们能通过一些手段让测试与开发更加同步, 尽量缩短滞后性, 包括让测试人员与开发人员更紧密合作, 尽量让测试用例自动化等.

       

      Q: 我还是觉得在开发迭代过程中, 测试人员的工作量不饱满.

      A: 如果这不是您的感觉, 而是事实, 并且前面测试人员必须要做的工作也都做了, 还是不饱满, 那么恭喜你, 可以省下一些测试人员, 去做别的事了. 但不推荐的是, 不要让测试人员同时为两个团队工作. 这会大大增加沟通的成本. 你会经常发现, 当你的开发者想找测试人员协助时, 却找不到人了, 于是你的团队便被堵塞在那里. 而测试人员本身的Context切换也是痛苦的.

       

      Q: 你们说验收测试应该由客户来编写, 可在我们这里根本不可能.

      A: 验收, 当然是由客户来验收, 这在理论上是毫无疑问的, 而且肯定在各行各业发生着. 只是具体到测试用例的编写和执行, 无论是自动化的还是非自动化的, 都需要掌握一定的技术, 需要周密的思考, 需要专门的时间, 客户可能无法同时满足这几个条件, 我们要尽力争取, 争取不到, 便只好通过更充分的交流来弥补越俎代庖的失真. 这时业务分析人员和测试人员要通力合作, 完成验收测试的编写.

       

      Q: 你们说你们之前的项目产品代码和测试代码的比例大约 1:3, 这不是平白增加了 3 倍的工作量吗?

      A: 是增加了 3 倍的代码量而不是工作量. 它节省了你几十人做几个月庞大的预先设计的工作量, 节省了你详细设计每个模块并为之编写几百页详设文档的时间, 节省了无数不眠之夜通宵Debug的时间, 它节省了集成阶段修复难以计数的Bug的工作量, 甚至它缩减了你产品代码的数量, 大量的重复代码被消除了, 大量过度设计的复杂代码被废除了, 你的代码更易理解了, 添加新特性更容易了, 发现的Bug更易定位了, 以致于大大减少了长达数年的生命周期内维护的工作量. 有点夸张了? 可这就是 TDD 和敏捷开发带给我们的好处(如果你已经实践了)和vision(如果你还在观望)

       

      Q: 我们也做单元测试, 但是是先写产品代码后写测试的. 难道非得 TDD, 非得测试先行吗?

      A: 没什么事是非做不可的. 取决于你要什么. TDD 只是以可验证的方式迫使你将质量内建在思维中, 长期的测试先行将历练你思维的质量. 而事后的单元测试只是惶恐的跟随者.

      发表于 @ 2008年07月13日 23:47:00|评论(loading...)|收藏

      新一篇: 敏捷质疑: 持续集成 | 旧一篇: 假冒的艺术

      评论

      #liuzuofei 发表于2008-07-14 10:00:59  IP: 218.6.169.*
      TDD和单元测试是一回事嘛?

      无能你是不是TDD,只要稍有经验或者规范的开发组都会有单元测试-》集成测试的过程,不是吗?
      #chelsea 发表于2008-07-15 00:33:54  IP: 221.216.145.*
      确实不是一回事, 事后的单元测试较TDD失去大半的意义
      #orbit 发表于2008-07-16 20:42:21  IP: 222.67.120.*
      把 private 变成 public 并不是一个好方法,做测试时应该从这个类派生一个花瓶类,然后将测试用力声明称花瓶类的友员。

      不过,用TDD开发的代码就根本不会存在测试不到的private函数
      #poscard 发表于2008-07-16 21:30:00  IP: 222.129.48.*
      养成先写单元测试再写实现的习惯后,就会感觉不写单元测试心里就很不安,心里很不踏实。
      #hawk_e2e 发表于2008-07-16 21:38:23  IP: 58.63.89.*
      提个问题:
      既然测试用例是校验需求和设计是否吻合的最佳描述,那么测试用例是否应该由设计人员编写会比较合适?
      否则,设计人员要听测试,一般设计人员就是开发人员了。
      #chelsea 发表于2008-07-16 22:28:59  IP: 221.218.103.*
      "做测试时应该从这个类派生一个花瓶类,然后将测试用力声明称花瓶类的友员"

      这个不行吧, 子类访问不了父类的 private 函数, friend 又不传递. g++ 4.3.1 和 vc 9 都报错

      class Private{
      void call(){}
      };

      struct PrivateTestCase;

      class SubPrivate : public Private{
      friend struct PrivateTestCase;
      };

      struct PrivateTestCase{
      void test(){
      SubPrivate().call();
      }
      };

      int main()
      {
      PrivateTestCase().test();
      return 0;
      }

      #chelsea 发表于2008-07-16 22:32:01  IP: 221.218.103.*
      "提个问题:既然测试用例是校验需求和设计是否吻合的最佳描述,那么测试用例是否应该由设计人员编写会比较合适?"

      测试用例是校验需求和实现是否吻合的最佳描述, 所以由开发人员编写比较合适
      #VisualUnit 发表于2008-07-17 09:41:27  IP: 119.130.157.*
      象访问私有成员,测试抽象类这些问题,通过修改产品代码的方式来实现,在应用中会存在一些问题。一种比较理想的解决办法是不动产品代码,而是拷贝产品代码,测试的是拷贝后的代码,这样你就可以随便加工被测试代码了,并且可以把测试代码直接放在产品代码的后面,不但很容易解决私有成员,抽象类之类的问题,也可以避免源文件中定义的符号难于外部访问、C语言的静态方法/变量不能外部访问等问题。不妨参考一下Visual Unit 2的工作方式。
      #chelsea 发表于2008-07-17 09:55:06  IP: 219.133.0.*
      没人去修改产品代码, 所有的操作都是在测试环境中完成的。 你说的拷贝是真的拷贝一份代码吗? 怎么维护同步?为什么不直接在测试代码中 #include 你的 .c .cpp文件?
      #VisualUnit 发表于2008-07-17 11:19:12  IP: 119.130.157.*
      "没人去修改产品代码, 所有的操作都是在测试环境中完成的。" 不修改产品代码,你能实例化抽象类吗?你能在代码中插装以便统计覆盖率吗?你能在用例中控制底层函数做某种指定的动作吗?实际工作中还有很多其他问题。另外,我在N年前,刚开始做单元测试的时候就是#include源文件的,但是在源文件中#include源文件,一些情况下会出问题并且很难解决(我是指在实际应用中,一个项目可能有几百上千个源文件,不是用一两个简单的源文件来试试)。至于维护同步,简单得很,写个工具监视源文件的修改,重新生成拷贝不就行了吗?
      #chelsea 发表于2008-07-17 15:34:49  IP: 219.133.0.*
      "不修改产品代码,你能实例化抽象类吗?"

      不确定什么情况下你需要实例化抽象类。 如果只是想测试抽象类中的代码, 可以在测试环境中从抽象类派生具体类来测, 产品代码不用改

      “你能在代码中插装以便统计覆盖率吗?”

      这个由预处理工具来做, 只在需要统计覆盖率的时候, 在编译产品代码前工具负责增加一个预处理的环节,以产品代码作为输入, 以插桩后的代码作为输出, 编译连接插桩后的代码就行了, 产品代码不用改。

      “你能在用例中控制底层函数做某种指定的动作吗?”

      可以参考 mockcpp mockpp 等实现

      “但是在源文件中#include源文件,一些情况下会出问题并且很难解决(我是指在实际应用中,一个项目可能有几百上千个源文件,不是用一两个简单的源文件来试试”

      单元测试, 每个测试源文件只 #include 它对应的产品源文件,这个关系是线性的,有什么问题? 不会把产品源文件的依赖也 include 了吧?
      #yeahspyme 发表于2008-07-18 12:55:47  IP: 203.187.184.*
      TDD类似于先把需求刻成模具,再往里填代码,这样就保证了产品的指令;哪天需要产品换个样了,我们还能从模具看到它原来是什么样子的。
      #1073X 发表于2008-07-27 09:47:36  IP: 220.248.129.*
      不要过于迷信TDD,维护测试代码给开发工作带来的巨大负担是不能用“难道这不是应该的吗?”这样的话敷衍的。这也是很多程序员不愿意作单元测试,并且抵制TDD的原因(虽然他们两者根本不是一回事)。在开发过程中确定什么应该用测试用例描述,什么可以不用,是很重要的。一段产品代码与之对应的测试代码的粒度,数量,都是需要斟酌的问题。不过这也给产品代码的设计提供了契机---不易于测试的代码,肯定不易于维护,扩展或者重构。
      修改产品代码,增加“桩”,对其进行测试。简直不可理喻!如果一定要测试私有的方法,用MOCK把私有方法暴露出来就好了。不过维护MOCK给开发带来的负担也不小,还是尽量不要这样做了。思考一下是不是应该把私有方法提取到一个类中作为公有的,才是正途。
      统计单元测试覆盖率?!这有什么意义?可以给你的文档增加更多的数据吗?或者让你对自己的代码更有信心?无聊之极.务实的程序员,不要给自己的工作增加无谓的负担。
      TDD的单元测试的确发现不了很多缺陷,那是因为在整个开发过程中出错的几率降低了。测试用例可以帮助程序员很好的把握程序的细节,而不是发现更多的错误,因为很多错误不会产生。另一方面,当你思考“我的代码到底在做什么?”的时候,不妨看看用例。
      TDD很好很强大,覆盖了需求,设计,编码,几乎就是大半软件开发生命周期。如果在代码完成之后也加上若干用例进行测试,那么也可以覆盖到测试阶段的一小部分。
      #1073X 发表于2008-07-27 10:03:32  IP: 220.248.129.*
      补充下:尽管TDD可以覆盖到软件开发的大部分过程,但是她还是很有限的,最好不要有用TDD代替需求文档(用例,卡片),设计文档(文字,类图),测试(系统)的想法。TDD可以在这些方面提供更多的保障,但是不是他么你的替代品。
      #liput 发表于2008-07-29 08:39:44  IP: 222.240.162.*
      我的理解是:
      单元测试是模块化结果后对各自独立的模块进行需求分析的实例化.
      需求首先通过分析确定之后,还需验证,后面还需要更多的修改以及验证.以往的工作中不使用单元测试,仅靠软件工程师或程序员来通过自然语言,图或代码的形式来理解所编写的程序代码是否满足要求,然而不仅测试难度大,要求技术水平高,而且还不利于统计和分析.
      因此,需要完整的单元测试框架来支持这种单元测试,其实单元测试在我们最初写代码的时候就有了,正是因为这种自动化单元测试框架的出现才使得单元测试如此大显神通的.
      同时,单元测试所要求的"隔离性"也很好理解,在模块化分析时我们的需求中就要求了我们的模块最好是能够相互独立的.
      #orbit 发表于2008-08-01 21:55:04  IP: 58.38.218.*
      谢谢提醒,我忘了这一点,protect成员可以,private成员不行。不过我还是强调一点,如果使用TDD方法,就不会存在测试不到的private函数,因为所有写出来的代码都是为了满足测试用例而写的
      发表评论  


      登录
      Csdn Blog version 3.1a
      Copyright © 切尔斯基