【IT168 技术文档】
引言
在实际的项目中,我们会遇到如下的问题:
1. 在编写一些不太好调试的程序时,如WebService 和一些后台运行的程序时,如何调试?
2. 在某些架构中存在一些底层框架,这些框架如何来调试?
3. 当我们编写一个方法或者一些存在互相引用的方法,如何从上到下进行一次调试?
4. 当编写出来一个方法后,难道现在你还在使用如下步骤调试?编译、F9断点、运行、调试?难道你不觉得好费时间啊。。。?或者你觉得为了一个方法不值,将一堆方法一起来调试/测试,这样难道不会遗漏一些方法也就是会遗漏一些BUG?
5. 如果我们要使用单元测试工具来测试一些底层的东西,通常正常的流程是编写一个单元测试模块[Test],然后利用单元测试工具执行来测试。但是这也是单元测试的一个弊端,太费时间。
……
基础
第一:既然我们这篇文章使用的单元测试工具是TestDriven.NET,那么总的先下载一个安装吧,下载地址:http://www.testdriven.net/download.aspx。
第二:这里我不跟大家将单元测试理论,也不讲如何使用单元测试工具TestDriven.NET进行单元测试。
这篇文章的核心是调试,好了,废话不多说,开始吧。
安装完TestDriven.NET后,在NET工程中,代码.cs 文件中弹出的右键菜单,将会多出三个选项,例如下图:
这里也顺便说一下新增的三个选项:
Run Test(s):点击后,在下方的信息栏中将会显示测试结果:
Test With:点击后将会显示子菜单:Debugger 使用NET的调试器调试;.NET 1.1将此方法运行在NET1.1框架下;Coverage 将会启动TestDriven.NET 自带的NCoverExplorer 进行代码覆盖分析;In_Proc 不太清楚有什么用。
Repeat Test Run:重复测试。
当第一次运行TestDriven.NET 后,在系统托盘栏中将会出现如下图标:
当右键点击此图标,可以进行TestDriven.NET 工具进程的操作:
我们来开始使用TestDriven.NET 来调试我们的第一个方法吧:
在我们要测试的方法F9增加断点,右键,选择Test With->Debugger,然后可以看到调试到断点处了:
然后F10/F11单步调试吧,在下方监视窗口中可以正常进行监视的:
也就是说,当使用TestDriven.NET 的Debugger 功能调试时,可以进行一个方法的调试,是不会运行其它方法的。
很省时间吧。
上面介绍了TestDriven.NET的Debugger 功能的基本使用方法,那么再来一些调味料吧。
几种调试方式
内嵌调试
所谓内嵌调试是指在方法中有参数的情况下采取的一种简便的调试方式。
例如我们需要调试如下方法:
public int CountAAddB(int a, int b)
{
return a + b;
}
为了调试这个方法,我们采取在方法内部初始化参数的方法来调试。
public int CountAAddB(int a, int b)
{
a = 2;
b = 3;
return a + b;
}
然后利用TestDriven.NET 调试。
内嵌调试就是调试包含输入参数的方法时,将输入的参数在方法内部赋值,调试/测试完毕后,再删除内部的赋值。这种方法相当节省时间,但是不利于重复调试/测试。
其实内嵌调试并不能算是一种很正规的调试/测试方法,但是我们编程过程中会遇到如下问题:第一:当一个方法返回类型为void 时,你编写单元测试代码如何去测试其正确性?第二,当一个方法是private 时,你用单元测试工具编写测试相当麻烦(网上有人写过用反射来测试private方法,但是相当麻烦)。而内嵌调试在方法内部快捷而简单。
这里我还想说一点的是,我们调试的预设值调试完毕后,注释掉,方便下次变动的时候在此调试。这样避免忘记调试/测试用例。
内接调试
内接调试就是指内部接口调试。例如我们需要测试CountAAddB 方法,我们编写了一个新的测试方法:
public void TestCountAAddB()
{
int i = CountAAddB(1, 2);
}
然后调试这个方法,跟进到CountAAddB 方法里面。
内接调试就是编写桩模块来调试被调试方法,在桩模块中断点,然后跟进到被调试方法中。这种方法需要增加一个额外的调试方法,可以进行重复调试/测试。
这个编写的测试方法,可以在要调试类里面,也可以在要调试类外面。
外接调试
外接调试就是指外部接口调试。例如我们需要调试CountAAddB方法,这个方法在ProjectCount 中,我们新建一个新的工程TestProCount,然后在这个工程里面引用ProjectCount 工程,在新建工程TestProCount 定义调试方法TestCountAAddB,断点后然后步进到ProjectCount工程中的CountAAddB 方法进行调试。
烤串调试
烤串调试就是指测试某一个基于其它方法的基方法,就像调试一串烤串。例如我们要测试如下代码中Count方法:
public int CountAAddB(int a, int b)
{
return a + b;
}
public int CountAMultiplyB(int a, int b)
{
return a * b;
}
public void Count()
{
int i = CountAAddB(1, 2);
int j = CountAMultiplyB(3, 4);
int ij = i + j;
}
Count 方法应用了两个方法:CountAAddB和CountAMultiplyB,通常我们调试Count方法,也就完成了对CountAAddB和CountAMultiplyB的调试。
调试FAQ
当调试包含参数的构造函数类时,为什么调试不了?
的确,当调试一个类,该类包含参数构造函数但是不包含无参数构造函数时,断点是进不去的,因为该类的默认参数没有赋值和初始化,如何解决这个问题,有两种方法。
第一种,将该类的含参的构造函数注释掉或者添加一个无参的构造函数。
第二种,采用内接调试方法实例化包含参数的类,然后调试。
几种停止调试的方法
在上图中,可以看到有两种,一种是直接点击工具栏中的停止按钮,第二种就是【工具】->【About Test Run】 选项。
后序
也许我上面所给出来的几种方法难登大家法眼,我只是将自己的一些东西写出来给大家分享。为什么我会这么做?在我们项目过程中,一面要承受工程进度的压力,一面又要考虑到软件质量的问题,我们需要在这两者之间找到一个平衡,为此我们要寻找一种既保证质量,又不是很费时间的调试/测试方法来解决这个矛盾。
我上面所给出的几种调试方法都是相当简洁的调试方法,能够在较短时间内调试/测试所编写的方法,虽说并不完美,但是也相得益彰,通常是编写一个方法,马上就调试/测试所编写的方法,保证了方法的有效性和正确性。
我所列举出来的几种调试方法都是相当基础的方式,至于怎么去用还得靠自己,复杂的东西都是基础变过来的。
最重要的是我们要养成把质量放在第一位的思维模式,要养成调试/测试的良好习惯,保证软件的质量和项目的进度。