自動化測試關鍵字驅動的原理及實現

   自動化測試如今愈來愈趨向於平臺化,平臺化是致力於協同工做,提升效率,讓更多人蔘與自動化的一個過程,在我看來,平臺化中,有一個更爲關鍵點,就是關鍵字驅動,只有把自動化測試的代碼轉換成爲你們更容易懂的天然語言,才能讓更多不懂代碼的人加入進去,才能達到平臺化的目的。今天咱們就來談談自動化測試中關鍵字驅動的原理及實現方式。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)等。也有很大的優化空間留給你們自行發揮去吧!

相關文章
相關標籤/搜索