昨天尝试向Nikey解释多态的用途,以失败告终。究其原因,一是没找到一个好的例子,二是Nikey实在没啥耐心听大段的长篇大论。现在尝试以博客的形式解释。
Nikey从事软件测试工作,脚本语言的初学者,写过一些小型自动化脚本,习惯于面向过程的思维(以函数来封装变化),还不太习惯于写伪代码,凡事要搞清楚细节,至少知道应该怎么实现。于是考虑,描述问题用的场景应该实用,贴近她的工作(而不是写什么bank,account之类的书本案例),伪代码不能太伪,至少她要知道怎么实现。
好,我们开始。Nikey最近的任务是网站测试,一个最简单的test case,打开一个网站,然后点击后退。这是一个国外的网站,所以要求同时兼容IE和Firefox。
首先,我们把测试脚本写出来。脚本很简单,描述每一个测试步骤。
{
Navigate();
ValidateNavigate();
Back();
ValidateBack();
}
接下去要考虑的事情,就是看怎么实现具体的方法了。很幸运地,VS开始支持录制脚本了(类似QTP),所以我们不需要太担心如何调用浏览器的navigate和back操作,录制就好。针对navigate这个动作,我们针对IE和Firefox分别录了两次。
{
//在IE打开网站
}
void NavidgateInFF()
{
//在Firefox打开网站
}
但是在试图把这两个函数统一起来的时候碰到了点问题,VS能抓到IE的对象,所以NavigateInIE里面是对象调用。但VS抓不到FF的对象,所以NavigateInFF是以模拟鼠标点击的方式实现的。(以上纯属虚构,主要想表达的是,NavigateInIE和NavigateInFF这两个方法区别很大,没有任何共同点可以抽取,或者说抽取公有函数的价值不高。)同样的BackInIE()和BackInFF()也会碰到类似的问题。
那么我们的test script会变成什么样呢?
switch (browser)
{
case "IE":
NavigateInIE();
case "FF":
NavigateInFF();
}
ValidateNavigate();
switch (browser)
{
case "IE":
BackInIE();
case "FF":
BackInIE();
}
ValidateBack();
}
test script的可读性瞬间变差了N个级别,test script里面我们应该只关心测试需要做的事情(打开网站),而不要关心事情怎么做(怎么能让IE打开一个网站)。我们试着把事情怎么做这些细节封装起来。
{
Navigate(browser);
ValidateNavigate();
Back(browser);
ValidateBack();
}
void Navigate(string browser)
{
switch (browser)
{
case "IE":
NavigateInIE();
case "FF":
NavigateInFF();
}
}
void Back(string browser)
{
switch (browser)
{
case "IE":
BackInIE();
case "FF":
BackInFF();
}
}
我们可爱的test script又回来了,很好维护,测试要调用的时候也很方便。在两个浏览器里面测试,只要调用下面两句代码即可。
SimpleTestCase("FF");
整个世界干净了,但是似乎发现了那么点坏味道。我们说,代码尽量不要copy & paste,这会影响代码维护。但是回顾Navigate和Back这两个方法,会发现它们的共同点非常多,这样的代码有问题吗?
如果不发生变化,代码其实无所谓可维护性,反正是给机器看的。但据说不会变化的只有变化本身,于是...
市场部经理提出,现在Chrome的占有率比Firefox高了,我们应该把它列入到测试范围。看看我们需要做什么:
1、增加(录制)NavigateInChrome方法
2、增加(录制)BackInChrome方法
3、修改Navigate方法,为Chrome增加一条选择支
4、修改Back方法,为Chrome增加一条选择支
5、增加test script的调用,SimpleTestCase("Chrome")
其中3和4,它们的修改会非常相似,都是需要增加类似下面的代码。
DosomethingInChrome();
想像一下,如果我们在测试过程中,用到的浏览器功能不只是navigate和back,还有其他的100个浏览器功能,那增加对Chrome支持的时候,我们不是要把上面的代码复制102次??有什么办法能解决这个问题呢?