您的位置:首页 > 其它

调测工具VcTester攻略--快速定位问题

2010-03-10 14:39 417 查看
调测工具VcTester攻略之
快速定位问题
作者:网际游民,成稿于2010年2月

关于本系列文章
本系列文章罗列如下:
《调测工具VcTester攻略之:搭建敏捷开发环境》
《调测工具VcTester攻略之:使用快捷编辑功能》
《调测工具VcTester攻略之:快速定位问题》
《调测工具VcTester攻略之:敏捷开发方式下调测组织过程》
《调测工具VcTester攻略之:脚本驱动API测试》
《调测工具VcTester攻略之:使用脚本桩构造数据驱动测试》
撰写本系列文章时得到ezTester技术专家Sundy多次帮助,在此谨表感谢!

敏捷开发模式下调测习惯的变化
这个问题我专门拎出来讲,大家可能在网上看到过一些资料介绍敏捷开发很少开调试器,不少开发人员3个月甚至半年都不做单步跟踪。很少开调试器是敏捷开发的必然现象,大家知道为什么吗?答案与敏捷开发模式下调测习惯发生了重大变化有关。
初级的敏捷开发要求持续交付,深度的敏捷开发则是以个体为单位的高频度迭代,每次迭代都有测试跟进。如果有人问:我们走的是敏捷流程呀,但产品质量怎么下降了呢?只一种可能,你尚在敏捷初级阶段,持续交付做到了,但进度与质量下降了,为什么呢?因为开展敏捷是有损耗的,目标工作量是限定的,如果目标进度定死,你只能损失软件质量了。深度敏捷则不然,你可能会损失一点进度(或者进度限定,损失一点工作量),但研发质量绝不会下降,下降了的绝不能叫深度敏捷。
上面提到敏捷模式下调测习惯发生变化,指的是深度敏捷,如果是初级敏捷,你的调测习惯很可能几乎不变,原先该怎么开发现在还怎么开发,无非出版本频度加快而己。
根据笔者经验,开发者个体的功能迭代要达到10多分钟(或更短)就完成一轮,深度敏捷才发挥功效,如果是半天或一天才完成一轮迭代,测试设计无论如何是难以跟进的(或免强跟进了但付出重大代价)。
好了,现在我就介绍每10多分钟就迭代一轮情况下,调测习惯有什么变化。首先,你不专为测试写脚本,当敏捷机器快速转动时,每轮迭代你的产品都是可运行、可展示的,驱动运行、驱动展示的是什么?手工操作吗?如果是手工操作,多半意味着你的深度敏捷到了难以为继的程度。在深度敏捷下,你写脚本首先是为了展示功能,用脚本生成配置数据、用脚本发起功能展示,然后,当你还有余力时,把这样的脚本转化测试用例。这里,根据本人的体验,而非教科书上说辞,随时展现功能在敏捷实践中要优先于正规的测试设计,在某些情况下,比如被测对象比较简单,即使你没编写正式测试用例,但有一堆用于展示功能的脚本,其实并不妨碍敏捷开发目标达成,因为形成正规用例的价值在于重用,如果被测对象很简单,调测过程是否重用已不再重要。
其次,深度敏捷开发中,大部分问题是由脚本驱动的调测行为发现的,而且多数是通过阅读代码方式定位的。因为产品质量始终处于准发布状态,新冒出一个问题你首先回顾最近更改是否导致问题,如果每次迭代添加(或修改)的代码量很小,完全不必依赖单步调试方式定位问题。敏捷开发深度越深,迭代步幅越小,你就越不依赖调试器去定位问题。事实上,单步调试非常耗时耗力,每冒出一个问题都在源码直接看出来,立即解决,那效率有多高!
由于上述调测习惯变化,日常调测中我们每遇到一个问题,首先会简单分析其原因,调测脚本写错了也是可能的,看看被测源码,尝试立即定位问题,如果问题比较复杂,一下子难以定位,你也不必急于开调试器,而应增加若干用例,试着缩小问题范围,将复杂问题分割、简化,让它变成简单问题。

让被测系统受控退出
在《调测工具VcTester攻略之:搭建敏捷开发环境》中已介绍一个被测系统,main函数如下:
int main(int argc, char *argv[])
{
    char buff[256];
    
    printf("Please input 'exit' to quit/n>>>");
    scanf("%s",buff);
    
    return 0;
}


现在优化一下,让这个控制台程序是否退出受CSE脚本控制。改成:

int IWantExit = 0;

int main(int argc, char *argv[])
{
    while (! IWantExit) Sleep(1000);
    
    // check and free resource
    return 0;
}


只要IWantExit值为0,主线程就一直循环Sleep,在VcTester集成界面,我们只需运行脚本“vd.IWantExit = 1”,被测系统随即主动退出。如此改造的好处有二,一是便于用脚本实现自动化控制,二是,让被测系统安全的主动退出,而非强制关闭,退出前你既可以用脚本控制全局资源依次释放,也可以在上面“// check and free resource”位置编写C/C++代码释放资源。


发起调测
在VcTester界面编写任意脚本,然后选中执行就能发起调测,调测脚本服务于各次迭代开发,最好按一定规则去组织,方便于维护。最简单的方式是把脚本定义到test函数中,你可以定义许多个test函数,对于同名脚本函数新定义的总会覆盖历史已存的。如下图,编写test函数后,把编辑光标挪到函数体内,按“Ctrl + A”快捷键即选中这个函数体,再按“Ctrl + E”执行后,test函数定义立即被更新。之后,用鼠标双击左侧快捷面板中“Run test()”命令(下图用红圈圈出的就是),刚定义的test函数立即被执行。



按上述方式组织调测脚本是最原始的,当你根本不考虑将调测脚本转化为正式测试用例时,可采用这种方式。
正常情况下,我建议你按规范测试用例方式去组织调测脚本,操作步骤在VcSmith用户手册基础篇的“6.1定义测试集与测试用例”一节有介绍。一个规范的测试用例代码如下:
class TTest1(TCase):
  func __init__(me,Owner):
    me.TCase(Owner,me);
  end;
  
  func run(me):
    assert(vd.MyAdd(3,5) == 8);
  end;
end(CurrSuite);

把编辑光标挪到这个定义体中,按“Ctrl + A”选中定义体,再按“Ctrl + E”执行它,一个测试用例就定义好了,而且,按VcTester缺省配置,当有用例更新时,系统会自动运行它的。
在VcTester中调试脚本与测试脚本没有本质差别,无非测试脚本要求用assert语句对结果值进行判断,所谓测试嘛,要知道运行效果是否预期,在调试脚本的基础上增加assert判断即可。所以VcTester的测试用例框架完全可用于编写调试脚本,调试完成后你追加若干assert语句就变成正式测试用例了。

快速定位问题
前面提到敏捷开发不必依赖调试器,定位问题很简单,多数情况下往被测代码插入若干print语句就够用了。VcTester将插入打印语句的操作也程式化了,用起来很方便,下面简述操作步骤。
首先,把“函数调用参数打印检视”开关打开,这一步只需操作一次。用鼠标双击快捷面板的“Enable Argument Inspect”命令,如下图:



接着,把需要打印函数传入参数与返回值的函数从函数调用图中拖入Inspected breakpoints列表,如下图示例,我们从函数调用树中,把BubbleSort与CObjData::compare两个函数用鼠标拖入到左侧列表。



之后,用CSE脚本正常发起调测,纳入观察的函数被调用时,其参数与返回值随即被打印输出。在打印输出区按“Ctrl + W”热键,可将打印信息转化为图形方式显示,如下图:



左侧显示函数调用树,precheck节点是函数调用刚进入时打印的,postcheck是函数返回时打印,前者传入参数被打印,后者返回值被打印。

除了参数与返回值可打印,全局变量,甚至经过加减等运算的表达式也可纳入打印,表达式先被watch,出现在watch列表后就可以像全局变量一样添加进来。详细操作请参考VcTesterMini用户手册第3章:函数调用检视。这里不管我描述得有多细,都没有人家手册写得全,大伙自个慢慢看去。
VcTester这项功能我用得奇爽无比!因缺少数据统计,我不敢断言每位使用VcTester的人都不会因为把测试做完整而拖累研发进度,我本人的感觉是:测试做完整了,研发进度也提前了。关键原因在于:我把别人消耗在联调、单步跟踪上的大量时间节约出来了,尽管频繁迭代过程(被测系统反复重起肯定会额外占用时间的)引入了一些损耗。

更深入定位问题
VcTester的“函数调用参数打印检视”功能是借助脚本桩机制实现,同样借助脚本桩功能,我们可以让函数调用的入口precheck与出口postcheck做更多事情,不只是打印输出,你可以更改变量,调用特定C函数或脚本函数等。因为借助标准的打脚本桩操作来实现,VcTester手册有详细介绍,本文略过不提。



相关文章:


《调测工具VcTester攻略--搭建敏捷开发环境》
《调测工具VcTester攻略--使用快捷编辑功能》
《调测工具VcTester攻略--快速定位问题》
《调测工具VcTester攻略--敏捷开发方式下调测组织过程》
《调测工具VcTester攻略--脚本驱动API测试》
《调测工具VcTester攻略--使用脚本桩构造数据驱动测试》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: