关键
1.为什么你会遇上这些bug?因为它们是你放的。
2.在TDD(测验驱动的开发)中,你会在一个严厉的反应循环中,开发测验与出产代码。
3.TDD或许有助于避免恼人的Zune bug。
4.方针硬件瓶颈有多种方式,你能够在严厉的TDD反应循环中,用TDD来避开瓶颈。
5.TDD协助你保证自己的代码如希望那样运转。但假如不是这样,你该怎样树立一个牢靠的体系?
6.TDD快速地发现小的和大的逻辑过错,避免呈现bug,使终究得到较少的bug。
咱们的作业办法都是编写代码,然后尽力让它运转起来。先树立,然后改错。测验是今后的事,即写完代码后才要做的事。在不行预期的调试作业上,大约要花掉咱们一半的时刻。在日程表上,调试作业都穿戴测验与集成的外衣。它是危险与不确认性的一个来历。批改了一个bug或许会发生另一个bug,有时乃至是一连串的bug。
坚持调试的计算有助于猜测要花多少时刻才干消除bug。你要衡量和办理bug。看曲线的拐点,拐点表明了趋势,告知你终究批改的bug要比发生的多。拐点表明的是现已做的事,但你永久不知道是否在代码的某个昏暗旮旯还躲藏着其它的丧命bug。
可制作性规划的一个方面是确以为什么你会有这些bug。答案很简略:过错是咱们放进去的。这便是咱们的作业办法。在开发今后的测验时,就会发现问题(图1和参考文献1)。咱们在开发时会制作过错,测验的作业便是找到这些问题。只需细心地测验,就会发现过错。开发后的测验作业意味着有必要找到、修正和办理许多的过错。
图1,在开发今后做测验时,会发现缺点
这种调试居后的编程程序是当今最常见的编程办法。先写代码,再调试它。调试居后的编程办法有危险。人都会犯过错。你既不能确认bug将在何时现身,也不能确认会花多长时刻才干发现它们(图2)。
图2,人都会犯过错。你无法确认bug何时呈现,以及要花多少时刻才干找到它们
当发现一个bug的时刻(TD)添加时,寻觅bug本源的时刻(TFIND)也会添加,一般添加得更多。假如从过错的引进到发现要花数小时、数天、数周,乃至数月时刻,你已遗忘了其时的布景,有必要开端做bug大扫荡。当你在开发周期以外发现缺点时,就有必要办理bug。关于有些bug,发现的时刻不会影响修正的时刻(TFIX),但有些代码的运转也或许依赖于bug,修正这些bug会形成其它bug。
短周期以及主动的测验主动化可节省时刻和作业量。这时,你再不需求重复深重而易错的手艺测验。有了测验主动化,重复测验简直不会添加额定作业量。测验主动化快速地探测出副作用,避免了对调试业务的需求。
另一种计划是TDD(测验驱动的开发),它在一个严厉反应的循环中开宣布测验代码与出产代码(参考文献2和3)。一个TDD微循环是:编写一个测验,未编译时调查该测验,做编译且测验失利,使编译通过,铲除任何剩余内容,并重复该进程直至完毕。编写测验代码与编写出产代码是整合的进程。假如犯了一个过错,没有通过新测验,你立刻就能够知道并改正过错。测验会告知你是否通过了新测验却发生了某个过错。在设备测验设备中参加主动化测验(图3),就能够自由地做重复测验。
图3,测验会告知你是否通过了新的测验,但却引进了一个bug。主动测验要刺进到一个单元测验设备中
在TDD反应回路中做开发与测验时,只能避免一部分bug的呈现,但不能完全消除。TDD对规划以及时刻的分配办法有着含义深远的影响。
与后调试的编程方式相反,TDD并不包含追寻过错的危险与不确认性(图4)。当发现一个过错的时刻接近于0时,寻觅过错本源的时刻也会趋于0。刚发生的代码问题一般清楚明了。假如不那么显着,则开发人员只需简略地康复刚做的修正,就能够回到一个可运转的体系。寻觅和修正过错的时刻和发生的时刻相同少,只要当程序员回忆随时刻而含糊,而且有更多的代码依赖于较早的过错时,作业才会变糟。
TDD为过错供给了即时的告知,可避免呈现许多要被逼追寻的bug。TDD可避免呈现缺点,然后调试编程会带来耗时耗力的调试作业。
Zune bug
TDD或许有助于避免恼人的Zunebug。微软公司的Zune是为了与苹果公司的iPod竞赛。2008年12月31日,Zune变成了“专为一天的程序块(abrick for a day)”。12月31日是新年前夜,是一个闰年的终究一天,这是30G Zune要阅历的第一个闰年。许多人都将Zune过错归因于时钟驱动程序中的一个函数。尽管列表1中的代码并非实践的驱动程序码,但它有相同的作用。你能够从列表1中Zune的无限循环中找到一些端倪吗?
图4,TDD关于规划以及时刻的运用有深远的影响。与调试居后的编程方式比较,TDD
没有回溯追寻bug的危险与不确认性
图5,对快速反应的需求使TDD微循环脱离方针硬件,而原生地运转在开发体系上。一个TDD循环包含两层方针的危险,但供给了快速TDD反应回路的长处
许多代码阅览专家检查了这个代码,并得出了或许与您相同的过错定论。闫年的终究一天是该年第366天,而Zune对这种状况的处理是过错的。在这一天,该函数永久不会回来!我编写了设定年份以及年中天数的代码,看是否像90%的Zune bug专家猜测的那样,将天数的布尔代码设定为等于或大于366就能处理问题。代码放入测验设备后,我编写了测验用例(列表2)。和Zune相同,测验进入了一个无限循环。我选用了通过数千名程序员审阅的恰当修正办法。出乎我的预料,测验失利了;设定年份与天数的测验以为日期是2009年1月0日。新年前夜,人们仍会具有自己的音乐,但Zune仍有个bug。
一次测验就能够避免Zune bug。可你怎样知道要去写这样一个测验?只要知道bug在哪里才会写测验。问题是,你并不知道bug在哪里;它们能够在任何当地。所以,这意味着你有必要为一切的部分写测验,至少是一切或许中止的当地。不可思议要考虑到一切需求测验的东西。但不用忧虑,你不需求针对全年每一天做测验。你只需求一个针对有关天数的测验。
计算机编程很杂乱,TDD能够体系化地让你的代码按原意运转起来,并供给能使代码作业的主动化测验用例。
嵌入规划
当我初次运用TDD时,我认识到,它或许有助于处理一个问题:方针硬件的瓶颈,这是令许多嵌入软件开发人员头疼的作业。瓶颈有多种方式,你能够运用TDD,在严厉的TDD反应循环期间避免瓶颈的呈现。许多嵌入开发作业都已完成了软硬件的并行开发。假如软件只能在方针硬件上运转,则或许糟蹋至少一次的时刻。例如,方针硬件或许迟至交给期还不行用,推迟了软件的测验;硬件或许贵重且稀疏;或许它本身就有问题。方针硬件还或许有长的树立时刻或长的上传时刻。大多数嵌入开发团队都遇到过这些问题,它们会减缓进展,并减少了树立今日杂乱体系的反应。
为避免方针硬件的瓶颈,能够选用“两层方针”法,即规划自己的出产代码与测验,使之大部分运转在规范PC上。但两层方针有自己的危险。开发体系中测验代码的信赖度是树立在交给给方针曾经的代码上。大多数两层方针危险是源于开发环境与方针环境之间的差异。这些差异包含对言语特性支撑的改变量、不同编译器的bug、运转时库的差异、文件名差异,以及不同的字长等。因为这些危险,你会发现,在一个环境下能无错运转的代码,或许在另一个环境下呈现测验过错。
不过,履行环境中潜在的差异不该成为阻挠选用两层方针办法的理由。相反,你能够在完成方针的路途中处理这些妨碍。嵌入TDD周期在不献身长处的前提下,克服了应战。
开发循环
当树立与测验循环只需几秒时刻时,TDD是最有用的。这种计划为大多数程序员排除了在循环中运用方针硬件的状况。快速反应的需求将TDD微循环与方针分脱离,而运转在开发体系上。图5显现了一个TDD循环,它包含着两层方针的危险,供给了快速TDD反应循环的长处。
表1中所列的各个阶段,估计能够在相应的阶段发现问题。例如,你会发现每个阶段都有助于找到这些问题。第1阶段会在你编程时给出快速反应,确认代码做你想要做的事。第2阶段保证你的代码是在两种环境下编译。第3阶段保证代码在主处理器和方针处理器上的运转相同。评价硬件或许需求比方针更多的存储器,这样才干把测验代码和出产代码都装入地址空间。有时候,假如你有一个牢靠的方针硬件,它有空间运转对单元的测验,也能够省掉掉第3阶段。第4阶段是在方针硬件上运转测验。在第4阶段能够引进一些依赖于硬件的单元测验。第5阶段是看你的体系完全整合时,是否如其应该的那样运转。至少让第5阶段的某些部分主动运转,这是一种好的主意。选用TDD的团队会发现第1阶段中的巨大价值,或许不要完成悉数各个阶段。
嵌入TDD循环并不能阻挠一切问题,不过它应有助于在恰当的阶段发现大多数刚刚发生的问题。你还应至少每个夜晚手动履行第2至第4阶段。接连的集成服务器(如Cruise Control或Jenkins)都能够调查你的源码库,在check-in后开端做树立作业。
TDD有助于保证你的代码做你想要做的事。假如不是这样,怎样才干树立一个牢靠的体系呢?它协助你让代码在最开端时坚持正确,它树立一个逐渐测验的组件,协助你保持代码的运转。你在发现、追寻和修正bug上要花掉相当多的时刻。许多开发人员现在都用TDD来避免这些bug的呈现。它基本上改变了你的编程办法。
TDD能快速地发现小的和大的逻辑过错,阻挠bug的发生,并终究得到较少的bug。较少的bug也意味着较少的调试时刻,以及较少的缺点。当新代码危及一个束缚或一个假定时,测验会告知你。然后,有杰出结构的测验会成为一种方式的可履行文档。
TDD还让你定心,这种决心来自于一个带有齐备回归测验组件的完全测验代码。选用TDD的开发人员称周末不再受搅扰,而且睡觉更好。TDD还监控进展,追寻当时的作业,以及做了多少作业。当代码变得难以测验时,它还对规划问题提出前期正告。