TDD记录下

在这之前,先澄清下什么是遗留代码。没有测试的代码就是遗留代码。如果代码根本没有UT,那么他是遗留代码。如果删掉一段代码,UT还是可以通过的话,那也是遗留代码,因为它的存在不影响测试,意味着它自身也没有被测试过。

从短期和长远两个角度来看,重构是可以不断改善代码质量,进而减少Defeat和提高开发速度。Clean Code应该是每个人发自内心的,碰到它就应该去清理它。有个基本原则了,Boy Scout Rule:“Leave the campsite cleaner than you found it”。你离开的时候,代码不能比你看到它的时候更糟。当我们在if…else…后面加一个if的时候,当我们在条件判断里面继续添加一个条件的时候,我们就应该问自己,自己加的是不是最后一根稻草,是不是又一次错过了重构的机会。

虽然我们鼓励重构,但这并不意味着可以随心所欲地去做重构。重构的前提是要有安全网,要在理解代码的情况下去做。安全网的构建可以通过编写UT来实现。如果我们的UT设计的足够好,能够覆盖所有的场景,那么对代码的修改就有一个安全网了,因为任何破坏既有逻辑的操作都会导致UT失败。而且编写UT的过程,是我们理解代码非常难得的机会。需要注意的是,重构与测试是环环相扣的,不要一次做过多的事情,每一个小的重构过后,执行一遍测试,保证所有的测试都仍然通过。在培训的练习过程中,依靠Eclipse不停地做一些小的重构,比如重命名,然后就执行测试用例,即便是Eclipse自己都能把一些简单重构自动完成。不过过分相信重构工具。

不要依赖于阅读代码去理解代码,而是通过编写测试去学习,因为代码的运行时行为很难掌控,而且很多记忆是短暂的。

下面列出重构的步骤。改Bug其实也可以按照下面的步骤来做,只是最后不是做重构,而是改变代码行为而已。

1.找出需要做重构的代码。寻找的依据就是坏代码味道。C语言可能有自己的坏代码味道,C++语言可能也有自己的坏代码味道,但还有很多味道是相同的,比如方法过长、参数过多、代码重复、条件复杂等。

2.找到测试点。我们需要圈定一个测试的范围。就是找出来那些测试是我们需要做的。这个时候覆盖率可能很重要,因为我们不能遗漏测试。

3.消除依赖。依赖于既有代码是否具有很好的可测试性,可能会花费一些时间。这里有一些消除依赖的技术可供选择:

  • 预编译宏,比如我们可以用cpputest_malloc替换malloc

  • UT编译时选用不同的文件。可以解决头文件不兼容问题。也可以强制使用UT的函数实现,或者类的实现。

  • 代码依赖于接口而不是实现。

  • 选择一个容易测试的边界。因为极有可能测试对象依赖于一个极其复杂的类,要去Mock它非常不容易。

4.编写测试。这个时候需要注意:

  • 给自己一个TODO列表,编写测试的过程中新发现的测试要加到TODO列表中去。

  • 一定不要现在去修改代码。

5.修改代码。经过编写测试,对代码的整个使用有了完整的理解之后,我们就可以动手修改代码了。这个时候注意,修改一定不要太大,不要去调试,不停去执行测试,根据测试的反馈决定下一步的动作。

6.重构。重构代码可能不是修改一次完成了。可能你随时会有更好的设计涌现出来,这个时候你可以宽心的继续去修改了。

你可能会问,我要是严格按照这个步骤,重构一定会成功么?答案不是肯定的。因为这里还是有可能因为漏掉测试,或者不正确的测试,导致造成新的Bug。但这不是你不使用它的理由。通过编写测试,可以加深你对代码的理解,可以帮助自己不停地验证,也可以帮助以后的人,它可以极大地增加你成功的概率。

补充问题:

很多人问一个问题,为了实现UT,我们可能需要为不同的编译器编写代码(解决系统调用、编译器方言等),我们可能需要写Mock类,这些都跟实际的产品代码无关,我们有必要花时间和精力去编写仅仅为测试的代码吗? “Test is good enough for reason”。因为我们省去了编译FW的时间,减少了后期Bug的数量,减少了定位问题的时间,减少了修复的时间。这样的减法和加法是值得的。

你可能感兴趣的:(TDD记录下)