教你如何写框架------元素管理篇
前言:
我的博客从来都是言简意赅,直接步入正题。
元素对象管理:
元素对象(以下称为locator)的维护与管理很麻烦,因为locator比较多,每个页面上要操作的可能有几十个,如何快速的查找及维护好能够 使我们写脚本的速度及维护速度大大提升。在前端开发中,开发人员通常是把UI样式放在CSS文件中,受此影响,我们也可以把我们的locator放在一个 专门的文件中,按照页面来分类,提取其公共的locator放在公共的文件中,这样或许可以提升些许编写脚本速度及后期维护成本,效果就是如果UI变了, 我们只需要修改对应的页面中的locator就行了,脚本都不需要重新编译(如果是用需要编译的语言,如JAVA),下面我将介绍一下如何放在专门的文件 中,如何解析该文件,及在脚本中如何调用。下面的脚本语言为JAVA。
1、文件类型------yaml
2、java解析yaml文件所需要的jar包:jyaml-1.3.jar,需自已在网上下载。
3、格式介绍:
a. baidu_input后面接上":",直接回车,然后空两格
b. type与value这两个key是固定的,后面接上":",然后空一格,也可以不空,如果value后面是xpath,建议用加上引号,具体去看下yaml的格式,百度一大堆。
c. 在webdriver中,有By.id,By.name,By.xpath,By.className,By.linkText等,我们选取这几种常见的,所以type的冒包后面可用的值为id,name,xpath
d. value的值为type中对应的类型的值,比如百度首页上的输入框的id='kw',所以在yaml文件中的写法如上图所示
4、解析上述的yaml文件:
import org.ho.yaml.Yaml; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.util.HashMap; public class Demo { private String yamlFile; public Demo() { yamlFile = "demo"; this.getYamlFile(); } private HashMap<String, HashMap<String, String>> ml; @SuppressWarnings("unchecked") public void getYamlFile() { File f = new File("locator/" + yamlFile + ".yaml"); try { ml = Yaml.loadType(new FileInputStream(f.getAbsolutePath()), HashMap.class); } catch (FileNotFoundException e) { e.printStackTrace(); } } }
可以在本地创建一个demo.yaml文件,保存在locator目录中,locator与src同级目录,然后写个main方法来调用一个 getYamlFile方法,可以看到解析demo.yaml后的值都赋给了变量ml。解析过程如此简单,解析速度如此之快,yaml文件也比较直观,这 是我选择用yaml文件的原因,当然可能还有其它更好的选择,大家可以自行尝试。
5、我们在写脚本时,元素对象一般是这样写的WebElement element = driver.findElement(By.id("kw"));所以接下来我们要把ml变量里的"value"转换成By对象。添加如下代码
private By getBy(String type, String value) { By by = null; if (type.equals("id")) { by = By.id(value); } if (type.equals("name")) { by = By.name(value); } if (type.equals("xpath")) { by = By.xpath(value); } if (type.equals("className")) { by = By.className(value); } if (type.equals("linkText")) { by = By.linkText(value); } return by;
这样通过ml中的type与value的值就对产生一个By对象。
6、By对象产生后,就可以把这个对象传给driver.findElement方法,继而生成一个WebElement对象.
import org.ho.yaml.Yaml; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.util.HashMap; public class Demo { private String yamlFile; public WebDriver driver; public Demo() { driver = DriverInstance.getInstance(); yamlFile = "demo"; this.getYamlFile(); } private HashMap<String, HashMap<String, String>> ml; @SuppressWarnings("unchecked") public void getYamlFile() { File f = new File("locator/" + yamlFile + ".yaml"); try { ml = Yaml.loadType(new FileInputStream(f.getAbsolutePath()), HashMap.class); } catch (FileNotFoundException e) { e.printStackTrace(); } } private By getBy(String type, String value) { By by = null; if (type.equals("id")) { by = By.id(value); } if (type.equals("name")) { by = By.name(value); } if (type.equals("xpath")) { by = By.xpath(value); } if (type.equals("className")) { by = By.className(value); } if (type.equals("linkText")) { by = By.linkText(value); } return by; } public WebElement getElement(String key) { String type = ml.get(key).get("type"); String value = ml.get(key).get("value"); return driver.findElement(this.getBy(type, value)); } public static void main(String[] args){ Demo d = new Demo(); WebElement element = d.getElement("baidu_input"); element.sendKeys(""); } }
7、到这里,已经成功了一半,因为已经把yaml文件中保存的元素成功的转化成了WebElement对象。但是还不够,接下来我们引入一下同步点的 概念,就是在调用locator时,保证locator是显示在页面上的,webdriver中有个WebDriverWait对象,代码如下:
private WebElement watiForElement(final By by) { WebElement element = null; int waitTime = Integer.parseInt(Config.getConfig("waitTime")); try { element = new WebDriverWait(driver, waitTime) .until(new ExpectedCondition<WebElement>() { public WebElement apply(WebDriver d) { return d.findElement(by); } }); } catch (Exception e) { System.out.println(by.toString() + " is not exist until " + waitTime); } return element; }
于是乎getElement方法里面就可以改为
public WebElement getElement(String key) { String type = ml.get(key).get("type"); String value = ml.get(key).get("value"); return this.watiForElement(this.getBy(type, value)); }
8、到这一步,又改进了一点,新的问题也随之产生了,watiForElement这个方法,返回的WebElement对象包括隐藏的,如果是隐藏的,那么在操作的时候,自然而然会报错,所以,我们得把隐藏的去掉,只显示displayed的元素对象,增加一个方法。
private boolean waitElementToBeDisplayed(final WebElement element) { boolean wait = false; if (element == null) return wait; try { wait = new WebDriverWait(driver, Integer.parseInt(Config .getConfig("waitTime"))) .until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return element.isDisplayed(); } }); } catch (Exception e) { System.out.println(element.toString() + " is not displayed"); } return wait; }
如此一来,getElement方法又可以改进一下了。
public WebElement getElement(String key) { String type = ml.get(key).get("type"); String value = ml.get(key).get("value"); WebElement element = this.watiForElement(this.getBy(type, value)); if(!this.waitElementToBeDisplayed(element)) element = null; return element; }
9、既然有等待元素对象显示的,那么反之就有等待元素对象消失的方法。
public boolean waitElementToBeNonDisplayed(final WebElement element) { boolean wait = false; if (element == null) return wait; try { wait = new WebDriverWait(driver, Integer.parseInt(Config .getConfig("waitTime"))) .until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return !element.isDisplayed(); } }); } catch (Exception e) { System.out.println("Locator [" + element.toString() + "] is also displayed"); } return wait; }
10、看上去一切很美好了,but....如果我们要验证一个元素对象不出现在页面上,就会出现问题了,于是增加一个方法
public WebElement getElementNoWait(String key) { WebElement element = null; String type = ml.get(key).get("type"); String value = ml.get(key).get("value"); try{ element = driver.findElement(this.getBy(type, value)); }catch(Exception e){ element = null; } return element; }
11、现在的问题是getElement与getElementNoWait的方法体很接近,于是我们来重构下这部分的代码,先增加一个方法,存放相同的方法体
private WebElement getLocator(String key, boolean wait) { WebElement element = null; if (ml.containsKey(key)) { HashMap<String, String> m = ml.get(key); String type = m.get("type"); String value = m.get("value"); By by = this.getBy(type, value); if (wait) { element = this.watiForElement(by); boolean flag = this.waitElementToBeDisplayed(element); if (!flag) element = null; } else { try { element = driver.findElement(by); } catch (Exception e) { element = null; } } } else System.out.println("Locator " + key + " is not exist in " + yamlFile + ".yaml"); return element; }
再把getElement与getElementNoWait方法进行修改
public WebElement getElement(String key) { return this.getLocator(key, true); } public WebElement getElementNoWait(String key) { return this.getLocator(key, false); }
12、到现在为止,已经可以满足绝大部分的需求了,完全可以使用了,下面的任务就是来点锦上添花了,举个例子,在yaml文件中,允许使用参数,比如
baidu_input: type: id value: "%s%s" baidu_button: type: id value: "%s"
在这里面的参数用%s来表示,于是在脚本中,我们调用getElement与getElementNoWait方法时需要我们把value给传进去,我们再来处理下这部分,增加一个方法。
private String getLocatorString(String locatorString, String[] ss) { for (String s : ss) { locatorString = locatorString.replaceFirst("%s", s); } return locatorString; }
在上面的方法中,我们可以看到,对于value值,我们是通过一个数组循环的去替代里面的%s,再把该方法结合到getLocator方法中去。
private WebElement getLocator(String key, String[] replace, boolean wait) { WebElement element = null; if (ml.containsKey(key)) { HashMap<String, String> m = ml.get(key); String type = m.get("type"); String value = m.get("value"); if (replace != null) value = this.getLocatorString(value, replace); By by = this.getBy(type, value); if (wait) { element = this.watiForElement(by); boolean flag = this.waitElementToBeDisplayed(element); if (!flag) element = null; } else { try { element = driver.findElement(by); } catch (Exception e) { element = null; } } } else System.out.println("Locator " + key + " is not exist in " + yamlFile + ".yaml"); return element; }
可以看到getLocator方法的参数变了,于是要重新的更改getElement与getElementNoWait方法,同时重载这两个方法。
public WebElement getElement(String key) { return this.getLocator(key, null, true); } public WebElement getElementNoWait(String key) { return this.getLocator(key, null, false); } public WebElement getElement(String key, String[] replace) { return this.getLocator(key, replace, true); } public WebElement getElementNoWait(String key, String[] replace) { return this.getLocator(key, replace, false); }
13、惊喜?更大的还在后面。再举个例子:
baidu_input: type: xpath value: "//div[@id='%productId%']//div" baidu_button: type: xpath value: "//div[@id='%productId%']//input[@name='button']"
类似于上面这种,整个里面都含有productId, 于是我们可以通过一个方法,调用这个方法后,里面的都会被替换掉,该方法如下。
public void setLocatorVariableValue(String variable, String value){ Set<String> keys = ml.keySet(); for(String key:keys){ String v = ml.get(key).get("value").replaceAll("%"+variable+"%", value); ml.get(key).put("value",v); } }
14、再比如,有一些元素对象是每个页面都会出现的,是公共的,这些公共的locator只是有时候要用到,大部分时候都不用,所以,我们把这些公共的放在一个特定的文件里,在需要的时候通过外部加载来使用这些公共的locator,增加一个变量与方法。
private HashMap<String, HashMap<String, String>> extendLocator; @SuppressWarnings("unchecked") public void loadExtendLocator(String fileName){ File f = new File("locator/" + fileName + ".yaml"); try { extendLocator = Yaml.loadType(new FileInputStream(f.getAbsolutePath()), HashMap.class); ml.putAll(extendLocator); } catch (FileNotFoundException e) { e.printStackTrace(); } }
15、到此为止,整个元素对象管理就结束了,这只是提供一个思路,大家如果有耐心从上到下的给按着来写一遍,应该会了解不少。最后来个总结性的代码。
1 public class Page { 2 3 public WebDriver driver; 4 5 private String yamlFile; 6 7 public Page() { 8 driver = DriverInstance.getInstance(); 9 yamlFile = "demo"; 10 this.getYamlFile(); 11 } 12 13 private HashMap<String, HashMap<String, String>> ml; 14 15 private HashMap<String, HashMap<String, String>> extendLocator; 16 17 @SuppressWarnings("unchecked") 18 protected void getYamlFile() { 19 File f = new File("locator/" + yamlFile + ".yaml"); 20 try { 21 ml = Yaml.loadType(new FileInputStream(f.getAbsolutePath()), 22 HashMap.class); 23 } catch (FileNotFoundException e) { 24 e.printStackTrace(); 25 } 26 } 27 28 @SuppressWarnings("unchecked") 29 public void loadExtendLocator(String fileName){ 30 File f = new File("locator/" + fileName + ".yaml"); 31 try { 32 extendLocator = Yaml.loadType(new FileInputStream(f.getAbsolutePath()), 33 HashMap.class); 34 ml.putAll(extendLocator); 35 } catch (FileNotFoundException e) { 36 e.printStackTrace(); 37 } 38 } 39 40 public void setLocatorVariableValue(String variable, String value){ 41 Set<String> keys = ml.keySet(); 42 for(String key:keys){ 43 String v = ml.get(key).get("value").replaceAll("%"+variable+"%", value); 44 ml.get(key).put("value",v); 45 } 46 } 47 48 private String getLocatorString(String locatorString, String[] ss) { 49 for (String s : ss) { 50 locatorString = locatorString.replaceFirst("%s", s); 51 } 52 return locatorString; 53 } 54 55 private By getBy(String type, String value) { 56 By by = null; 57 if (type.equals("id")) { 58 by = By.id(value); 59 } 60 if (type.equals("name")) { 61 by = By.name(value); 62 } 63 if (type.equals("xpath")) { 64 by = By.xpath(value); 65 } 66 if (type.equals("className")) { 67 by = By.className(value); 68 } 69 if (type.equals("linkText")) { 70 by = By.linkText(value); 71 } 72 return by; 73 } 74 75 private WebElement watiForElement(final By by) { 76 WebElement element = null; 77 int waitTime = Integer.parseInt(Config.getConfig("waitTime")); 78 try { 79 element = new WebDriverWait(driver, waitTime) 80 .until(new ExpectedCondition<WebElement>() { 81 public WebElement apply(WebDriver d) { 82 return d.findElement(by); 83 } 84 }); 85 } catch (Exception e) { 86 System.out.println(by.toString() + " is not exist until " + waitTime); 87 } 88 return element; 89 } 90 91 private boolean waitElementToBeDisplayed(final WebElement element) { 92 boolean wait = false; 93 if (element == null) 94 return wait; 95 try { 96 wait = new WebDriverWait(driver, Integer.parseInt(Config 97 .getConfig("waitTime"))) 98 .until(new ExpectedCondition<Boolean>() { 99 public Boolean apply(WebDriver d) { 100 return element.isDisplayed(); 101 } 102 }); 103 } catch (Exception e) { 104 System.out.println(element.toString() + " is not displayed"); 105 } 106 return wait; 107 } 108 109 public boolean waitElementToBeNonDisplayed(final WebElement element) { 110 boolean wait = false; 111 if (element == null) 112 return wait; 113 try { 114 wait = new WebDriverWait(driver, Integer.parseInt(Config 115 .getConfig("waitTime"))) 116 .until(new ExpectedCondition<Boolean>() { 117 public Boolean apply(WebDriver d) { 118 return !element.isDisplayed(); 119 } 120 }); 121 } catch (Exception e) { 122 System.out.println("Locator [" + element.toString() 123 + "] is also displayed"); 124 } 125 return wait; 126 } 127 128 private WebElement getLocator(String key, String[] replace, boolean wait) { 129 WebElement element = null; 130 if (ml.containsKey(key)) { 131 HashMap<String, String> m = ml.get(key); 132 String type = m.get("type"); 133 String value = m.get("value"); 134 if (replace != null) 135 value = this.getLocatorString(value, replace); 136 By by = this.getBy(type, value); 137 if (wait) { 138 element = this.watiForElement(by); 139 boolean flag = this.waitElementToBeDisplayed(element); 140 if (!flag) 141 element = null; 142 } else { 143 try { 144 element = driver.findElement(by); 145 } catch (Exception e) { 146 element = null; 147 } 148 } 149 } else 150 System.out.println("Locator " + key + " is not exist in " + yamlFile 151 + ".yaml"); 152 return element; 153 } 154 155 public WebElement getElement(String key) { 156 return this.getLocator(key, null, true); 157 } 158 159 public WebElement getElementNoWait(String key) { 160 return this.getLocator(key, null, false); 161 } 162 163 public WebElement getElement(String key, String[] replace) { 164 return this.getLocator(key, replace, true); 165 } 166 167 public WebElement getElementNoWait(String key, String[] replace) { 168 return this.getLocator(key, replace, false); 169 } 170 }

浙公网安备 33010602011771号