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

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

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

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

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

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

1.对象池:对象

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这个对象里,方面后面直接调用关键方法。(请你们试着用注解去实现,会更炫)blog

2.映射表:

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地点}”,这样就把关键字给明确的标注出来了,虽然从直观感觉上差了那么一点点,但倒是关键字驱动中很重要的一部分。接下来的问题转变成为了对一段字符串中${}的解析了:

正则类:

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;
    }
     
}

 获取关键字类:

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里:

读取这个命令:

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"));
	}
	
}

 

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

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类给改改:

public class MoveDesk {
	
	public void setZhangsan() {
		System.out.println("this is zhangsan");
	}

	public void moveDeskFromA2B(){
		System.out.println("this is test!");
	}

}

 

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

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

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

相关文章
相关标签/搜索