深入探究数据测试之:封装无关操作
作者:网络转载 发布时间:[ 2013/1/5 9:49:21 ] 推荐标签:
这篇文章,我们来谈谈如何封装这些无关操作。这里要讲讲我们为封装操作专门开发的框架SmartTest。
首先要知道,代码不可能无故减少,只能是被封装了而已。也是说,上一篇的第一个例子中间所做的所有的事情,一样都不能少,而是仅仅被搬到其他地方而已。
那么搬到什么地方了呢?我们这里提出一个概念,叫做测试应用。测试应用是把所有的测试的过程和被测试对象的建模行为封装好的一个类。这个类将所有的测试过程全部纳入到每一个类函数中,而把动态的信息暴露出来,允许用户设置。
如上一篇博文的第二个例子,underTest是一个测试应用。也许你会说,原来是把之前的代码全部搬入到underTest的go函数中啊,这简单。但是如果是仅仅的这么搬家,那么这个封装太无聊了。
做搬家之前,我们将会对整个测试过程进行阶段划分:
如上图,我们可以看出,我们使用测试应用,定义了一个自动化测试用例的运行的三个阶段:setup, run和teardown。每一个阶段又划分了若干步骤:
Setup的cleanEvironment步骤:负责清理环境,确保这个测试用例可以正确执行。好的习惯是每一次测试用例运行完毕之后清理一次,但是却不能保证如果测试用例运行失败或者中途退出导致没有进行环境清理,而且很多情况会需要现场环境进行错误排查,所以我们将清理环境放在了每个测试用例之前。
Setup的generateConfig步骤,被测试对象总有多多少少的config配置,这一步是将这些配置设置成为测试用例需要的配置,然后放置在合适的地方。
Setup的generateData步骤,这一步是至关重要的步骤,因为我们是数据测试,这个阶段需要将需要的数据生成出来,放在合适的位置,等待被测试程序取用。我们后面的章节会对这一个阶段做的事情进行更加全面的讲解。
Setup的StartRefService步骤,将本次测试的依赖服务启动起来,可能是mysql,可能是httpd,看被测试系统的应用场景。
Setup的StartService步骤,如果被测试对象是一个影子进程,而你测试的是这个影子进程的配置项,那么每次测试的时候,你都需要重新启动这个进程,以确新的配置项被加载。
Run的sendInput阶段,有些被测试程序是被动接收数据的,比如一个http服务,或者一个监控某一个目录的影子进程,那么这样的被测对象,要启动它,只需要将数据发送给他好了。所以这个步骤是用于发送测试数据。
Run的execute阶段,大部分被测试对象是属于需要主动运行的,比如hadoop或者hive脚本,需要执行一个脚本,来启动被测试对象,那么这种启动的代码,放在这个步骤中。
Run的getResult阶段,
Teardown的stopService阶段,用来关闭被测试对象的影子服务。
Teardown的stopRefService阶段,用来关闭之前启动的被测试对象依赖的服务。
以上的阶段和步骤的划分,只是对整个自动化测试过程的一个划分,针对不同的被测试对象,步骤执行也是不同的,并不是每一个步骤的需要有代码,而是选择合适的步骤好。
按照这个思路,我们改造之前的代码:
<?php
class underTest
{
function go()
{
$this->setup();
$this->run();
$this->tearDown();
}
function setup()
{
$this->cleanEvironment();
$this->generateConfig();
$this->generateData();
$this->startRefServices();
$this->startService();
}
function run()
{
$this->sendInput();
$this->execute();
$this->getResult();
}
function teardown()
{
$this->stopService();
$this->stopRefServices();
}
function cleanEvironment()
{
exec('rm -rf /table_path/staff');
}
function generateConfig(){}
function gnerateData()
{
$input = get_user_input();
file_put_contents('/tmp/staff.table', $input); //将输入的数据写入到文件
}
function startRefServices(){}
function startService(){}
function sendInput()
{
exec('hadoop fs -mkdir /table_path/staff/'); //在hadoop上建立目标文件的目录路径
exec('hive -e "create external table stuff(name string, age bigint, salary bigint) partitioned by (dp string) location '/table_path/staff/dp=etao';"'); //建立hive的建表语句
exec('hadoop fs -put /tmp/staff.table/table_path/staff/dp=etao/part-000'); //将我们的数据文件放到hdfs上
exec('hive -e "alter table staff add if not exists partition ( dp=etao ) location ’/table_path/staff/dp=etao"');//调用alter table为我们的表增加一个分区,地址是刚刚上传的文件(hive的专有特性)
}
function execute()
{
exec('underTestShell.sh'); //执行被测试脚本
}
function getResult()
{
exec('hadoop fs -cat /tmp/result.table/table_path/result/dp=etao/part-000 > /tmp/result.tmp'); //将结果表的结果下载下来
$result = file_get_contents('/tmp/result.tmp'); //结果表读取到内存中
}
function stopService(){}
function stopRevServices(){}
}
?>
你也许会说,这不是将我之前写好的代码拆成几个步骤吗?是的,但是这么做好处多多:
1、规范。划分成这样的阶段和步骤之后,测试人员可以找到自己应该在哪个阶段写入代码,维护其他测试人员代码的时候,也可以找到相应的阶段了。
2、可分步骤运行。如果测试应用定义了启动相关服务,而测试用例运行的时候,只需要启动一次服务,启动之后的其他测试用例运行无需启动,这种情况下,我们可以在test dirver中,选择性的运行这一步骤。其他步骤也是一样的。
3、更加大局的看,这么做将来可以将每个步骤需要做的工作,抽象成模块,供给其他测试应用使用。
如果你有心,你会发现,可以用面向对象的思想将这些步骤的定义提取到一个父类去,然后以后只需要继承好。
在接下来的一章,我们将会讲对于数据的抽象和封装。

sales@spasvo.com