首页
|
新闻
|
新品
|
文库
|
方案
|
视频
|
下载
|
商城
|
开发板
|
数据中心
|
座谈新版
|
培训
|
工具
|
博客
|
论坛
|
百科
|
GEC
|
活动
|
主题月
|
电子展
注册
登录
论坛
博客
搜索
帮助
导航
默认风格
uchome
discuz6
GreenM
»
测试测量
» 嵌入C语言的测试驱动开发:为什么要调试?
返回列表
回复
发帖
发新话题
发布投票
发布悬赏
发布辩论
发布活动
发布视频
发布商品
嵌入C语言的测试驱动开发:为什么要调试?
发短消息
加为好友
Bazinga
当前离线
UID
1023230
帖子
5213
精华
0
积分
2607
阅读权限
70
在线时间
158 小时
注册时间
2013-12-20
最后登录
2015-10-22
金牌会员
UID
1023230
1
#
打印
字体大小:
t
T
Bazinga
发表于 2015-1-24 11:16
|
只看该作者
嵌入C语言的测试驱动开发:为什么要调试?
如何
,
硬件
,
开发
,
C语言
要点
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还监控进度,追踪当前的工作,以及做了多少工作。当代码变得难以测试时,它还对设计问题提出早期警告。
收藏
分享
评分
the king of nerds
回复
引用
订阅
TOP
返回列表
电商论坛
Pine A64
资料下载
方案分享
FAQ
行业应用
消费电子
便携式设备
医疗电子
汽车电子
工业控制
热门技术
智能可穿戴
3D打印
智能家居
综合设计
示波器技术
存储器
电子制造
计算机和外设
软件开发
分立器件
传感器技术
无源元件
资料共享
PCB综合技术
综合技术交流
EDA
MCU 单片机技术
ST MCU
Freescale MCU
NXP MCU
新唐 MCU
MIPS
X86
ARM
PowerPC
DSP技术
嵌入式技术
FPGA/CPLD可编程逻辑
模拟电路
数字电路
富士通半导体FRAM 铁电存储器“免费样片”使用心得
电源与功率管理
LED技术
测试测量
通信技术
3G
无线技术
微波在线
综合交流区
职场驿站
活动专区
在线座谈交流区
紧缺人才培训课程交流区
意见和建议