<!--[if !vml]--><!--[endif]-->验证码给自动测试带来了很大的问题,但也并不是完全不能解决。结合我们在上文讨论的验证码实现的方法,图4给出了验证码实现的大致原理图。
从图4中可以看到,从技术的角度来看,至少设计两种不同的方法来实现自动测试工具对验证码的处理:
<!--[if !supportLists]-->1、 <!--[endif]-->完全从客户端角度考虑,靠模式识别的方法识别出验证码图片对应的字符串;
<!--[if !supportLists]-->2、 <!--[endif]-->从服务端角度考虑,如果自动测试工具可以获取Session中存储的随机数,也就能正确处理验证码了。
这两种方法是解决自动化测试中验证码问题的主要方法,我们分别称其为识别法和服务端插入法。这两种方法在实现方法上侧重点不同,适用的场合也不同。
识别法完全不用考虑服务端应用的实现,通过各种技术方法对显示的验证码图片进行“破译”,这样,即使完全不能接触到服务端代码,也能让自动化测试在有验证码的情况下进行下去;但这种方法当然也有其致命的缺点:只能对简单的验证码进行识别,对复杂的验证码,根本就无法识别。
而服务端插入法则从服务端入手,通过提供一个额外的客户端接口,向客户端只需要知道该接口的调用方法,就能通过该接口来获取该页面的验证码图片对应的实际数据,并使用该数据继续测试。
另一方面,除了技术角度解决问题的方法以外,还可以通过一些非技术的方法来解决验证码问题。
<!--[if !supportLists]-->4.1 <!--[endif]-->识别法的实现
识别法适用于不能获得和改变服务器端代码的情况下,在这种情况下,由于服务端代码本身不可获得,或是不能对其进行修改,测试者只能完全从客户端的角度想办法解决验证码的问题。
识别法的核心是对验证码图片的模式识别算法,该算法的可实现性基本取决于图片本身的复杂程度。以本文前面列举的验证码示例来说,类似Gmail和Hotmail的验证码基本上是无法通过程序来识别的。而最简单的验证码实现,例如ASP下用xbm技术生成的图片,就可以很容易地通过算法来识别;在PHP、dotNET等平台上完全使用图形库函数生成的图片,同样可以通过对某个区域内的图片分析,识别出图片对应的实际数字或是字母。
下面以处理xbm格式的验证码为例,介绍对其进行识别的算法。
本文的2.1节对xbm文件格式进行了深入的探讨,用xbm实现验证码的方法在ASP和dotNET平台上非常常见,由于xbm文件格式的规则性,因此很容易通过程序对其进行识别。一般的识别过程如下:
<!--[if !supportLists]-->多次访问带有验证码的页面,分析每次获得的xbm文件和显示的图片之间的对应关系,获得验证码中所有符号对应的十六进制串;
<!--[if !supportLists]-->编写识别验证码的代码,识别代码根据获得的xbm文件,将其按照编码方式分组,然后与上一步骤中获得的对应的十六进制串进行比较,这样就可以识别出该xbm文件对应的验证码的实际数据。
下面这段代码是用于将xbm图片文件识别为相应的验证码内容的C语言代码:
int getDigital(char dig[10])
{
const char orgdig[10][10] = {
{0x3c,0x66,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0x66,0x7e}, //0
{0x18,0x1c,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00}, //1
{0x3c,0x66,0x60,0x60,0x30,0x18,0x0c,0x06,0x06,0x7e}, //2
{0x3c,0x66,0xc0,0x60,0x1c,0x60,0xc0,0xc0,0x66,0x38}, //3
{0x38,0x3c,0x36,0x33,0x33,0x33,0xff,0x30,0x30,0xfe}, //4
{0xfe,0xfe,0x06,0x06,0x3e,0x60,0xc0,0xc3,0x66,0x3c}, //5
{0x60,0x30,0x18,0x0c,0x3e,0x63,0xc3,0xc3,0x66,0x3c}, //6
{0xff,0xc0,0x60,0x30,0x18,0x18,0x18,0x18,0x18,0x18}, //7
{0x3c,0x66,0xc3,0x66,0x3c,0x66,0xc3,0xc3,0x66,0x3c}, //8
{0x3c,0x66,0xc3,0xc3,0x66,0x3c,0x18,0x0c,0x06,0x03} //9
};
int i=0, j=0;
int ret = 1;
for(i=0; i<10; i++)
{
ret = 1;
for(j=0; j<10; j++)
{
if(orgdig[i][j]!= dig[j])
{
ret = 0;
break;
}
}
if(ret)
return i;
}
return -1;
}
主函数:
char picc[500], t[40], od[10];
char separators[] = ",";
char *token, *endstr;
int i=0, j=0;
//获取需要识别的图片中的数据描述部分,内容为
//0x3c, 0x3c, 0x18, 0x3c, 0x66 …
//将其存放在字符串picc中
…………
//分解获得的串
token = (char *)strtok(picc, separators); /* Get the first token */
if(!token)
{
return( -1 );
}
while( token != NULL )
{
if(!strcmp(token , "")) //处理为“空”的内容,将其替换成0x00
t[i] = 0x00;
else
t[i] = strtol(token, &endstr, 16);
i++;
token = (char *)strtok(NULL, separators);
}
for(i=0; i<4; i++) //一共4个数字,分别调用getDigital函数进行处理
{
for(j=0; j<10; j++)