好,接下来在一个一个展开来说。

  1.单元测试应该在物理设计阶段进行规划,而不是完成代码后。

  实践告诉我,单元测试是需要良好的设计来支撑的。一个耦合度很高的模块几乎没有办法进行单元测试。我曾经几次相对已有的代码进行一些重构来支持单元测试。终都放弃了。因为对这些耦合度很好的模块的重构总是会引入一些不可预期的问题。终投入都要远远超过我的预计。因此,我得出的经验是:单元测试需要在物理设计时期思考所涉及模块的可测试性,为了可测试性,需要对设计进行一些调整。往往这种调整都会使设计更好。因为,耦合性和可测试性是成反比的,因此可测试性越高,也证明耦合性越低。低耦合是目前大家已经公认的良好设计的标准。

  2.Mock类库一般情况下都是鸡肋

  我在开始推动单元测试的时候详细的研究了Rhino.Mocks类库。当时也被它强大语法能力所折服。并且实际将该类库应用在了我们项目的单元测试中。可是,过了一段时间后,当我再次需要使用Mock对象的时候。我才发现,我自己写一个Mock对象的成本其实非常低。远低于学习Rhino.Mocks抽象的语法的成本低。因此,我建议你除非能够确认你每天(至少每周)都要用到Mock对象。否则,建议不要使用Mock类库。

  因为,Mock类库的接口设计往往和我们开发人员(尤其是静态类型语言开发人员)的思维方式不一致。一段时间不用这些类库的时候,你会忘记他们抽象的语法。需要再付出时间去学习他们的语法。但是,对于一些特定的测试场景,编写简单的Mock对象的成本本身非常低的。往往5分钟可以写出来自己用着很爽的Mock对象。

  但是,不推荐使用Mock类库,不等于你不需要学习和了解Mock类库。因为学习他们的接口会对你自己设计Mock对象非常有帮助。

  3.对已有代码编写单元测试的难度非常大

  因为我们做的是控件产品,在兼容性方面的要求很多时候会很苛刻。有时候一个产品发布之后,发现了Bug,下一个版本也要保证这个Bug原封不动的表现在那里(这时候,我们会说这是我们产品的一个Design)。因此,对代码的重构会成本很高。因此,要想在不破坏原有结果的情况下进行单元测试的难度非常大了。这一点,也许有我们产品的特殊性所在。但是,我觉的目前现实中的很多项目其实和我们的项目的要求还是很像吧。

  4.当单元测试很多的时候,组织和命名会比较有挑战。

  我一直没有建立起来一套好的单元测试命名体系。目前在项目中的组织方式是:两个平行的工程,产品工程使用InternalVisibleToAttribute为测试工程提供Internal成员的访问权限。两个工程保持相似的组织方式。但是,当一个被测类型很庞大的时候,测试代码很难组织好了。

  5.目前很少遇到单元测试影响重构的情况

  不好的单元测试或过度测试都会对重构带来不好的影响,在我参与的上一个项目中出现过这种情况。当时,项目突击了一段时间的单元测试。硬任务,每人必须写nnn个单元测试。后来,产品升级的过程中,不断的删除原有的单元测试。那是因为后来没有人将单元测试作为指标了。否则,可能很多有价值的重构都会不做了。因为修改单元测试太费劲了。这一点对我造成了阴影。以至于我在当前项目的前期没有很高调的推动单元测试。这是这个项目过程中我大的遗憾之一。

  也许是因为,单元测试覆盖率较低;也许是因为我们没有拿单元测试再作为指标,因此大家写的单元测试的质量更高了。总之在当前这个项目的升级中,似乎很少发现维护单元测试付出较大的情况。