一个类 A,我要对它某方法(取名为x)的代码进行测试,在x中会创建 窗口 B,并且执行B中的y方法。
代码如下:
ClsA.cs
class ClsA
{
public void x()
{
FrmB oF = new FrmB();
oF.y(); int j = 0;
int k = 5 / j;
}
}FrmB.cs
public partial class FrmB : Form
{
public FrmB()
{
InitializeComponent();
} public void y()
{
MessageBox.Show("运行了很多复杂代码!");
}
}测试代码ClsATest.cs
/// <summary>
/// x 的测试
/// </summary>
[TestMethod()]
public void xTest()
{
ClsA target = new ClsA();
target.x();
}这个测试存在的问题:在ClsA.x()中,启动了 FrmB ,并且“运行了很多复杂代码!”。
代码如下:
ClsA.cs
class ClsA
{
public void x()
{
FrmB oF = new FrmB();
oF.y(); int j = 0;
int k = 5 / j;
}
}FrmB.cs
public partial class FrmB : Form
{
public FrmB()
{
InitializeComponent();
} public void y()
{
MessageBox.Show("运行了很多复杂代码!");
}
}测试代码ClsATest.cs
/// <summary>
/// x 的测试
/// </summary>
[TestMethod()]
public void xTest()
{
ClsA target = new ClsA();
target.x();
}这个测试存在的问题:在ClsA.x()中,启动了 FrmB ,并且“运行了很多复杂代码!”。
ClsA.cs
class ClsA
{
IfFrmB oF; public ClsA(IfFrmB pF)
{
this.oF = pF;
} public void x()
{
////FrmB oF = new FrmB();
this.oF.y(); int j = 0;
int k = 5 / j;
}
}
MockFrmB.cs
class MockFrmB : IfFrmB
{
public void y()
{
// 啥事都不干,直接返回即可
return;
}
}测试代码ClsATest.cs:
/// <summary>
/// x 的测试
/// </summary>
[TestMethod()]
public void xTest()
{
ClsA target = new ClsA(new MockFrmB());
target.x();
}
想问各位大师,这种做法对吗?如果对,那这种做法有几个明显问题,如何避免?
比如,ClsA 就会多一个 IfFrmB 变量(并且我这一次是模仿书中代码,在类构造函数就引入),对较大的系统来说,不见得是个好事。
interface IfFrmB
{
void y();
}
与
class MockFrmB : IfFrmB当然,我也可以对void x() 进行改变:
ClsA.cs
class ClsA
{
public void x(IfFrmB oF)
{
oF.y(); int j = 0;
int k = 5 / j;
}
}测试代码ClsATest.cs:
/// <summary>
/// x 的测试
/// </summary>
[TestMethod()]
public void xTest()
{
ClsA target = new ClsA();
target.x(new MockFrmB());
}这是不是已经足够好了?是不是还有更好的方案更简洁地“自己”建立mock?
我主要是对 《单元测试之道C#版-使用Nunit》第6章对Now的做法的疑问。我本来是想按它的建议进行调整,但实际尝试中,按它的做法将对接口进行改变,很多编程习惯都不能遵守。这个应该不是理想的。我这几天调整出来的实际的结构,仍然按我对 MessageBox 的处理那样。先按几个原则:一、代码测试覆盖率追求100%;二、测试一个方法,不测试它调用的“复杂”方法(例如会产生用户操作的,弹出界面的,代码较多的,相关类较多的)。
用一个静态测试属性来表明当前处于测试状态,对于复杂方法,直接使用测试员预计的值返回(预计一个方法返回值应该不是困难的事情)。这样测试某个“方法A”时,测试员列出对复杂方法的预计值,这样测试代码跳过这些复杂方法直接返回预计值,我们就可以达到仅测试“方法A”本身。
存在的问题就是,因为判断是否在测试状态,所以代码覆盖率就不可能达到100%。希望能引起大家的一些讨论。
而UI本身的测试,通常是用ui automation工具,来模拟鼠标、键盘的操作,看界面的反应是否正常,这个和一般的代码测试是不同的。
"只因为你是在重复造轮子。"
这是一个度的问题(或平衡问题)。
我的经验是,如果有能力自己造轮子的话,尽量自己造,这样你造车子时,就不会受限于轮子厂商。因为在VB阶段,我建立了自己的控件体系(文本框、按钮、工具栏……甚至整个界面),它们内部能协调配合,才能让我快速地完成企业中需要的系统。(两个例子:一是郭台铭发展过程中,就是因为有自己的模具加工厂,所以让他能比其它小工厂提供更客制化的产品。二是我们公司沙滩车的发动机就只能使用某公司的几款型号,我们公司没有能力制造发动机,研发人员都在说笑“如果对方不卖发动机给我们,我们就白开发了”)当然如果超出能力之外的,那才是依依不舍地放弃,比如我在VB中就没建立出UI的自动测试体系,原因是我对系统的API层,GUI相关API就没有深入掌握过,所以我的软件连产品终检都没有,质量保证连永远是我心中的痛。
“如果你注重测试的话,应该先写测试代码,再写实际程序”
惭愧,我还没有这种意识,更没有实际操作的经验。目前我看到VS2008能对单元测试提供这么方便的操作,就已经很欣喜若狂,要发展到那一层次估计还要一段时间。
虽然它有了灵活性,但站在程序员的角度上看,它让代码增加复杂性,而且增加的还不少。为了解耦,我要加入这么多代码,我感觉有些不划算。
而目前,我只是因为测试,所以对解耦有一定的需求,如果抛开自动化测试,这两个类基本上就是“同生共死”。所以我想的是,为了解耦,有没有一种方案,即能让代码复杂性不会提高很多,又能实现ClsB的替身注入,以我目前水平,就只有想到通过状态判断来进行分支。
我对业务层、用户层还是无法在实际中进行区分、应用。理论上讲都很简单,但当我面对几万行的代码中的时候,根本不会想到这是业务层还是用户层,让我与我的部下轻松地开发出用户可用的系统为首要任务。比如报表打印界面,这是业务层还是用户层,我就区分不出来,我们目前的做法只要求程序员先在一个自制的工具上配置报表(如报表所在的界面,报表模板的文件名,报表所用的SQL语句,报表长,报表宽……),然后在程序中写上 ClsA.DispReport(); 剩下就是ClsA的事情了,程序员可以不关心。ClsA甚至可以完成:如果操作用户点击报表预览时出错,可以让用户决定是否要把当前屏幕抓图发送给程序员。
当把这个ClsA做好,自己高兴都来不及了,就没有动力想着去区分业务层、用户层。