自动化测试关键字驱动的原理及实现

转载地址:http://www.cnblogs.com/zhangfei/p/5330994.html


自动化测试现在越来越趋向于平台化,平台化是致力于协同工作,提高效率,让更多人参与自动化的一个过程,在我看来,平台化中,有一个更为关键点,就是关键字驱动,只有把自动化测试的代码转换成为大家更容易懂的自然语言,才能让更多不懂代码的人加入进去,才能达到平台化的目的。今天我们就来谈谈自动化测试中关键字驱动的原理及实现方式。

       先来看一个例子,老师对着同学们说了一句:去把桌子从A地点搬到B地点。当老师发出这个命令后,发现没有人动,因为没有明确的对象,不知道这个命令是对谁发出的,于是老师再说了一句:张三,去把桌子从A地点搬到B地点。这时候,有了明确的对象--张三,有了命令的主体--搬桌子从A到B。所以这句” 张三,去把桌子从A地点搬到B地点”中就有两个关键字:张三、把桌子从A地点搬到B地点。当这两个关键字缺少任何一个,这件事就不可能完成。所以,我们可以把这个场景理解为关键字驱动,假如我们把上面这句话转换为代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MoveDesk {
     
     private Person zhangsan;
 
     public void setZhangsan(Person zhangsan) {
         this .zhangsan = zhangsan;
     }
 
     public void moveDeskFromA2B(){
         zhangsan.getTheCommandFromTeacher();
         zhangsan.findTheDeskAtA();
         zhangsan.moveDeskToB();
         zhangsan.notifyTeacherHadFinished();
     }
 
     public static void main(String[] args) {
         MoveDesk md = new MoveDesk();
         md.setZhangsan( "张三" );
         md.moveDeskFromA2B();
     }
 
}

 

上面的代码当然是伪代码,但从中可以看出,其为了达到一个目的,流程都是一样的,那两个关键字在main方法中体现为:张三对应md.setZhangsan("张三"),”把桌子从A地点搬到B地点”对应md.moveDeskFromA2B()。由此我们可以这样理解一下:每一个关键字对应一个具体的方法。所以,总结一下关键字驱动的实现特点:

1. 每一个关键字对应的方法(以下称为关键方法)必须能够随时被调用,这就像老师对张三发出的命令张三必须去执行一样。在JAVA里面为了达到这个关键方法随时能被调用,其类对象必须事先就被new好,所以要有一个对象池,这个对象池就像一个班级,有很多个学生可以供老师发出命令并去执行。

2. 关键字必须与关键方法进行关联,就像老师让张三搬桌子,而张三跑去买了一包烟,这就不对了,所以,必须要有一个关键字与关键方法的映射表。

3. 得有一个关键字的解析方式,比如上面那条命令中,我们要能够得出张三、把桌子从A地点搬到B地点这两个关键字,要是不能得出关键字,关键方法就会少执行,就如上面的伪代码中,如果去掉md.setZhangsan("张三")这一句,显然就会报空指针异常了。

4. 上面这三条具备后,就是执行了,老师发出的命令,张三要能够执行,当然,要是张三是个刺头就另说了。JAVA代码能够交给JVM去执行,但转换成为关键字后,JVM就不认识这些关键字了,所以,我们也必须要有一个健壮的执行引擎,让“张三,去把桌子从A地点搬到B地点”这一句话能够被执行成功。

综上所述,只要我们把上面的四条通过代码来实现,一个简单的关键字框架雏形就出来了,接下来我们一条条的实现。

1.对象池:

1
2
3
4
5
6
7
8
public class RegisterCenter {
     
     public static Map<String, Object> OBJ_POOLS = new HashMap<String, Object>();
     
     static {
         OBJ_POOLS.put(MoveDesk. class .getName(), new MoveDesk());
     }
}

 

将所有含有关键方法的类都初始化一遍,然后放到OBJ_POOLS这个对象里,方面后面直接调用关键方法。(请大家试着用注解去实现,会更炫)

2.映射表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class KeywordReflect {
     
     public static Map<String, Map<String, String>> KEYWORD_POOLS = new HashMap<String, Map<String, String>>();
     
     static {
         KEYWORD_POOLS.put( "张三" , KeywordReflect.methodInfo(MoveDesk. class .getName(), "setZhangsan" ));
         KEYWORD_POOLS.put( "把桌子从A地点搬到B地点" , KeywordReflect.methodInfo(MoveDesk. class .getName(), "moveDeskFromA2B" ));
     }
     
     public static Map<String, String> methodInfo(String className, String methodName){
         Map<String, String> methodInfo = new HashMap<String, String>();
         methodInfo.put( "class" , className);
         methodInfo.put( "method" , methodName);
         return methodInfo;
     }
     
}

 

说明:上面的KEYWORD_POOLS对象的数据结构是MAP里面套了一个MAP,这是因为要明确类对象,因为不同的类里可能有相同名称的方法,所以,为了避免混乱,必须标注好一个方法的类与方法名,这样才能建立好一一对应的映射表。(同样可以用注解来实现)

3.解析关键字

为了能够解析出关键字,我们得把” 张三,去把桌子从A地点搬到B地点”这句话的关键字给标注出来,改造一下”${张三},去${把桌子从A地点搬到B地点}”,这样就把关键字给明确的标注出来了,虽然从直观感受上差了那么一点点,但却是关键字驱动中很重要的一部分。接下来的问题转变成为了对一段字符串中${}的解析了:

正则类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class RegExp {
     
     public boolean match(String reg, String str) {
         return Pattern.matches(reg, str);
     }
  
     public List<String> find(String reg, String str) {
         Matcher matcher = Pattern.compile(reg).matcher(str);
         List<String> list = new ArrayList<String>();
         while (matcher.find()) {
             list.add(matcher.group());
         }
         return list;
     }
      
}

 获取关键字类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ParseKeyword {
     
     public List<String> getKeywords(String p){
         String reg = "(?<=(?<!\\\\)\\$\\{)(.*?)(?=(?<!\\\\)\\})" ;    
         RegExp re = new RegExp();
         List<String> list = re.find(reg, p);
         return list;
     }
     
     public static void main(String[] args) {
         ParseKeyword p = new ParseKeyword();
         System.out.println(p.getKeywords( "a${a}a" ));
         System.out.println(p.getKeywords( "a\\${a}a" ));
         System.out.println(p.getKeywords( "a${a\\}a" ));
         System.out.println(p.getKeywords( "a${a\\}a}a" ));
         System.out.println(p.getKeywords( "a${a}a${" ));
         System.out.println(p.getKeywords( "a${ab}a${a}" ));
     }
}

 

说明:里面用到了正则预查模式,正则预查模式是正则中的一种相对高阶的用法,掌握这个后,你的正则就会上一个台阶。

4. 执行引擎:

执行引擎先要找到需要执行的语句,所以,首先应该把老师发的那个命令给读取出来,这个命令可以存放在任何格式的文件里,只要能被读出来即可,在这里我们保存在command.txt里:

读取这个命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Executor {
     
     public List<String> readTxtFile(String filePath) {
         List<String> list = new ArrayList<String>();
         try {
             String encoding = "UTF8" ;
             File file = new File(filePath);
             if (file.isFile() && file.exists()) {
                 InputStreamReader read = new InputStreamReader( new FileInputStream(file), encoding);
                 BufferedReader bufferedReader = new BufferedReader(read);
                 String lineTxt = null ;
                 while ((lineTxt = bufferedReader.readLine()) != null ) {
                     list.add(lineTxt);
                 }
                 read.close();
                 bufferedReader.close();
             } else {
                 System.out.println( "找不到指定的文件" );
             }
         } catch (Exception e) {
             System.out.println( "读取文件内容出错" );
             e.printStackTrace();
         }
         return list;
     }
     
     public static void main(String[] args) {
         Executor e = new Executor();
         System.out.println(e.readTxtFile( "src/command.txt" ));
     }
     
}

 

读取之后,流程就是:获取关键字->获取关键方法->执行关键方法.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
public class Executor {
     
     private ParseKeyword pk = new ParseKeyword();
     
     public List<String> readTxtFile(String filePath) {
         List<String> list = new ArrayList<String>();
         try {
             String encoding = "UTF8" ;
             File file = new File(filePath);
             if (file.isFile() && file.exists()) {
                 InputStreamReader read = new InputStreamReader( new FileInputStream(file), encoding);
                 BufferedReader bufferedReader = new BufferedReader(read);
                 String lineTxt = null ;
                 while ((lineTxt = bufferedReader.readLine()) != null ) {
                     list.add(lineTxt);
                 }
                 read.close();
                 bufferedReader.close();
             } else {
                 System.out.println( "找不到指定的文件" );
             }
         } catch (Exception e) {
             System.out.println( "读取文件内容出错" );
             e.printStackTrace();
         }
         return list;
     }
     
     public void executor(){
         List<String> commands = this .readTxtFile( "src/command.txt" );
         for (String command : commands) {
             List<String> keywords = pk.getKeywords(command);
             for (String keyword : keywords) {
                 this .invoke(keyword);
             }
         }
     }
     
     public void invoke(String keyword){
         Map<String, String> keyMethod = KeywordReflect.KEYWORD_POOLS.get(keyword);
         String className = keyMethod.get( "class" );
         String methodName = keyMethod.get( "method" );
         Object obj = RegisterCenter.OBJ_POOLS.get(className);
         Method method = this .getMethod(methodName, obj);
         try {
             method.invoke(obj);
         } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
             e.printStackTrace();
         }
     }
     
     private Method getMethod(String methodName, Object obj) {
         try {
             Method[] methods = obj.getClass().getMethods();
             for (Method m : methods) {
                 if (m.getName().equals(methodName)) {
                     return m;
                 }
             }
         } catch (SecurityException e) {
             return null ;
         }
         return null ;
     }
     
     public static void main(String[] args) {
         Executor e = new Executor();
         e.executor();
     }
     
}

 

为了大家能够看到执行结果,把MoveDesk类给改改:

1
2
3
4
5
6
7
8
9
10
11
public class MoveDesk {
     
     public void setZhangsan() {
         System.out.println( "this is zhangsan" );
     }
 
     public void moveDeskFromA2B(){
         System.out.println( "this is test!" );
     }
 
}

 

这样执行之后,就能看到这两个关键方法体被执行了!执行引擎中最关键的就是利用了JAVA的反射来执行方法!

以上的代码经过测试运行通过!

由此一个最最简单的关键字驱动架子就搭建完成了,还有如下的一些点需要大家自行去完成:关键方法参数处理,关键方法返回值处理,异常处理,结合数据驱动,结合测试框架(TESTNG/JUNIT)等。也有很大的优化空间留给大家自行发挥去吧!

点亮测试人生!QQ:408129370

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值