打造第二代测试框架TestDriven 2.0(三)—— 测试还是调试?玩玩BreakPoint!

------------------ 

前言 Preface

------------------ 

本文是第二代测试框架系列文章,同时也是软件工程革命三部曲中的技术文献。

本文展示了Visual Studio中独特的断点调试技术,是目前尚未有人尝试过的断点方式,而且也是您google也找不到的技术。

 

------------------ 

测试还是调试?

------------------ 

测试驱动TDD的提出,有其理论的优势,也有明显的不足,本人的理解是:当我们对项目有六成以上的把握,采用测试先行可以节省代码开发量,否则绝对不建议测试先行(测试先行 不等于 单元测试 等等)。

原因也很简单,如果我们对未来估计不足,不仅系统代码变化很大,而且测试代码变化也很大,盲目采用了测试先行导致增加了工作量。

如果上面的论点是正确的,那么我可以获得下面的论点:项目开发中,测试先行以外,必然存在了测试后行,或者我们非常熟悉的名词——调试。我不知道为啥测试和调试有必要分开,这个就是玩文字游戏,硬把相同的事情用不同的LABEL区分。

那么既然测试后行就是调试,我就应该把调试技术纳入测试框架,于是引入了Breakpoint技术。

 

------------------ 

Breakpoint

------------------ 

传统的调试技术,无非就是在Visual studio里面设几个断点,然后运行中查看。

这让我想起我初中的时候,用qbasic写的人生第一个游戏:Dream of Navigation(当时玩大航海时代玩疯了,于是自己写了一个)。扯远了,提这个只是想说,那时已经有了break point技术了。所以,几乎写程序的人就知道breakpoint。

今天,老弟我就令走不寻常路,介绍一个新的断点技术。

首先看看在c#里面如何使用代码设置断点:

System.Diagnostics.Debugger.Break();

 

恩,就这么简单!当代码出现了这句话后,在debugger模式下,IDE会自动停在这里了。如果问原因,就是在IL里面插入了一个断点。换句话说,我可以用纯IL的方法实现这个技术,不过既然微软封装了,我就省了。

 

不知道大伙会不会立刻打开VS,然后写一段测试一下? 有了这个技术,就可以实现VS的任何断点技术,包括:断点条件、断点命中次数、甚至断点宏等。如果不知道这些功能,可以打开IDE,设一个断点,然后鼠标右键点击一下,得到:

 

 

平时,我几乎很少用上面的功能,因为实在太麻烦也不直观。如果这些设置能在代码里面完成,调试结束后删除调试代码,那么就更加实用了。于是我就扩展了断点技术。 

 

当然,遇到比较大的问题就是断点位置问题,比如计算命中次数,不同地方的断点对命中次数计算是不一样的,不过有了之前的经验,解决这个问题就很简单了,我只要获得当前断点代码在总代码中的偏移量,就可以获得唯一的位置标识符,然后隔离不同的算法。例如一下就是断点技术中另外一个核心算法:

代码
        public string GetBreakingLocation()
        {
            StackTrace stack 
= new StackTrace();

            StackFrame testcaseframe 
= stack.GetFrame(default_stack_index);

            System.Reflection.MethodBase method 
= testcaseframe.GetMethod();

            StringBuilder builder 
= new StringBuilder();
            builder.Append(method.DeclaringType.FullName);
            builder.Append(method.Name);
            builder.Append(method.IsGenericMethod);
            
if (method.IsGenericMethod)
                builder.Append(method.GetGenericArguments().Length);
            
foreach (System.Reflection.ParameterInfo parameter in method.GetParameters())
            {
                builder.Append(parameter.ParameterType.FullName);
                builder.Append(parameter.Name);
            }
            builder.Append(method.ReflectedType.FullName);
            builder.Append(testcaseframe.GetILOffset());

            
return Pixysoft.Security.MD5.GetMD5(builder.ToString());
        }

 

我使用 StackTrace 技术,获取了断点代码的偏移量(GetILOffset()),然后再MD5一下,就得到了当前断点的唯一标识符。

最后,展示一下断点技术的实际应用:

 代码


        
public void test012()
        {
            
string name = "hello";

            
//设置 立刻中断

            BreakPoint.BreakAtOnce 
= true;

            
// 当 name = "hello" 的时候,中断

            BreakPoint.BreakAt(name 
== "hello");


            
for (int i = 0; i < 100; i++)
            {
                
//当循环到98次开始 中断

                BreakPoint.BreakAt(
98);

                
if (BreakPoint.True)
                {
                    Console.WriteLine(i);
                }
            }


            
int value = 1;

            
for (int i = 0; i < 3; i++)
            {
                value 
= i % 2;

                
//当 value 值变化后中断

                BreakPoint.BreakWhen(value);

                
if (BreakPoint.True)
                {
                    Console.WriteLine(value);
                }
            }
        }

 

 

源代码下载:

http://www.boxcn.net/shared/8vpet6yf0d

 

------------------ 

后记 Conclusion

------------------ 

 本文和”框架“ 两字相比,简直是雕虫小技,不过Big Game在后头。各位别急着copy paste,也别急着让我出个完美版的XXX,这都仅仅是冰山一角。

 

希望各位能够继续关注我的TestDriven 2.0 框架,也请继续关注软件工程革命三部曲。我会好好玩一场游戏。

 

同时,我也希望各位ITer们(当然更多的是IT精英们),在忙着学hibernate / spring、找.net 十大工具、研究一大堆scrum之类名词等时候,试试在人挤人人推人的人流中,停下脚步,回头看看,当别人往左转的时候,试试往右转,之后就会发现原来你能做的比他们更好。期待各位的留言和想法。

 

鞠躬敬礼! 

posted @ 2010-03-02 03:36    阅读(1717)  评论(5编辑  收藏  举报
IT民工