欢迎使用Markdown编辑器
一、Web自动化原理
1、Selenium概述
Selenium是当前最流行的Web UI自动化测试框架,Selenium是一个工具包,支持多语言、多平台、多浏览器;它提供所有的网页操作API, 是一个功能库。
通过Selenium,可以编写出自动化脚本模拟用户(在浏览器里)操作web界面。 比如点击界面按钮,在文本框中输入文本,甚至一些拖拽操作,滚动界面的操作;还可以从web界面获取信息: 比如获取某个区域的文字内容,从而通过自动化脚本进行分析处理。
2、自动化测试原理
通过Selenium来实现Web自动化,其中的原理涉及到三个最主要的内容:自动化脚本、webdriver、浏览器。
- 自动化脚本:支持多语言,有python、java、js等;通过自动化测试脚本调用浏览器对应的API实现需要的功能。
- 浏览器驱动webdriver: webdriver像一个媒介,自动化脚本驱动webdriver,不同浏览器有不同的webdriver,例如火狐浏览器的FirefoxDriver,谷歌的 ChromeDriver。浏览器驱动 也是一个独立的程序,是由浏览器厂商提供。
- 浏览器:即我们平常用到的浏览器,如谷歌、火狐、IE等。不同版本的浏览器对应不同版本的webdriver。
自动化测试原理:自动化脚本通过webdriver 驱动调用浏览器,浏览器测试产品。浏览器将结果返回给驱动,驱动返回给自动化脚本。自动化的实现过程如下:
- 使用Selenium客户端库(或者自动化框架封装后的方法) 编写自动化脚本。脚本的自动化请求都是通过使用这个库的接口对象完成的,比如调用某个元素对象的click方法,就会发送点击这个元素的请求给 下方的浏览器驱动。
- 自动化脚本的请求通过客户端库,构建出相应的HTTP请求,发送给浏览器驱动。
- 浏览器驱动接收到我们的自动化脚本发送过来的界面操作请求后,会转发请求给浏览器, 让浏览器去执行对应的自动化操作。
- 浏览器执行完操作后,会将自动化的结果返回给浏览器驱动。
- 浏览器驱动再通过http响应的消息返回给我们的自动化脚本的客户端库。
- 自动化脚本的客户端库接收到响应后,将结果转化为数据对象返回给自动化脚本。我们编写的脚本就可以知道这次自动化操作的结果。
二、Selenium元素定位技巧
Webdriver通过findElement或findElements方法结合By类返回元素句柄来定位元素,findElement()方法返回一个元素,如果没有找到会抛出一个异常:NoElementFindException()。findElements()方法返回多个元素,如果没有找到,会返回空数组,不会抛出异常。通常,将找到的页面元素赋予到一个存储对象(WebElement)中,以便后续使用。
1.常用的元素定位方法
常见的元素定位方法有id、linkText、partialLinkText、name、tagName、xpath、className、cssSelector这8种,下文以java代码的方式分别对各种定位方式做介绍,其他编程语言,定位原理一样,切换API的表达方式即可。
定位方法 | Java语言实现实例 |
---|---|
id定位 | driver.findElement(By.id(“id的值”)); |
name定位 | driver.findElement(By.name(“name的值”)); |
链接的全部文字定位 | driver.findElement(By.linkText(“链接的全部文字”)); |
链接的部分文字定位 | driver.findElement(By.partialLinkText(“链接的部分文字”)); |
css方式定位 | driver.findElement(By.cssSelector(“css表达式”)); |
xpath方式定位 | driver.findElement(By.xpath(“xpath表达式”)); |
class名称定位 | driver.findElement(By.className(“class属性”)); |
TagName标签名称定位 | driver.findElement(By.tagName(“标签名称”)); |
下面分别对这几种定位方法做介绍,以一段搜索框HTML代码为例:
<input id="kw" name="wd" class="s_ipt" value="" maxlength="255" autocomplete="off">
新建一个driver对象:
WebDriver driver = new FirefoxDriver();
1.1 使用id定位
从上面搜索框HTML代码片段中,发现有个id="kw"的属性,就可以通过这个id定位到这个搜索框:
WebElement element = driver.findElement(By.id("kw"));
1.2 使用name定位
同理,搜索框HTML代码片段中,发现有个name="wd"的属性,我们也可以通过name属性定位到这个搜索框:
WebElement element = driver.findElement(By.name("wd"));
1.3 使用class定位
在搜索框的HTML代码段中,有className="s_ipt"的属性,我们就可以通过className这个属性定位到这个搜索框。通常说的class属性就是HTML代码中className属性。
WebElement element = driver.findElement(By.className("s_ipt"));
1.4 使用linkText定位
使用超链接定位,以下面的一段html代码为例:
<a class="nav" href="http://news.baidu.com" target="_blank">新闻</a>
上面是一段超链接代码,就可以通过超链接定位这个元素:
WebElement element = driver.findElement(By.linkText("新闻") );
1.5使用partialLink定位
这个方法就是模糊查询出来的超文本,比如一个网页中的所有超链接,所有都包含"jd"的:
WebElement element = driver.findElement(By.partialLinkText("jd") );
1.6 使用xpath定位
被测试的HTML代码:
<html>
<body>
<div id="div1" style="text-align:center">
<img alt="div1-img1" src="http:www.sougou.com/images/logo/new/sogou.png"
href="http://www.sogou.com">sogou image</img><br/>
<input name="div1input">
<a href="http://www.sogou.com">搜狗搜索</a>
<input type="button" value="查询">
</div>
<br/>
<div id="div2" style="text-align:center">
<img alt="div2-img2" src="http:www.baidu.com/img/bdlogo.png"
href="http://www.baidu.com">baidu image</img><br/>
<input name="div2input">
<a href="http://www.baidu.com">百度搜索</a>
<input type="button" value="查询">
</div>
</body>
</html>
使用上面代码生成被测试网页,基于此网页来实现不同的xpath页面元素定位方法:
1.6.1 xpath使用绝对路径定位
绝对路径表示页面元素在被测网页的HTML代码结构中,从根节点一层层地搜索到需要被定位的页面元素,绝对路径起始于正斜杠(/),每一步均被斜杠分割。
示例:在被测网页中查找第一个div标签下的“查询”按钮。
WebElement button = driver.findElement(By.xpath("/html/body/div/input[@value="查询"]"));
代码解释:上述xpath定位表达式从html dom树的根节点(html节点)开始逐层查找,最后定位到“查询”按钮节点。路径表达式“/”表示跟节点。
备注:使用绝对路径定位页面元素的好处在于可以验证页面是否发生变化。如果页面结构发生变化,可以会造成原先有效的xpath表达式失败。使用绝对历经定位是十分脆弱的,因为即便页面代码结构只发生了微小的变化,也可能会造成原先有效的xpath定位表达式定位失败。因此,建议在自动化测试的定位实施环节中,优先考虑使用后面将要介绍的相对路径进行定位。
1.6.2 xpath使用相对路径定位
相对路径的每一步都根据当前节点集之中的节点来进行计算,起始于双//。
示例:在被测试网页中,查找第一个div标签下的“查询”按钮。
WebElement button = driver.findElement(By.xpath("//div[@value='查询']"));
代码解释:上述xpath定位表达式中//表示从匹配选择的当前节点开始选择文档中的节点,而不考虑特面的位置。input[@value=“查询”]表示定位value值为“查询”两个字的input页面元素。
备注:相对路径的xpath定位表达式更加简洁,不管页面发生了何种变化,只要input标签的value属性值没变,始终都可以定位到。推荐使用相对路径的xpath表达式,并且越简洁越好,可大大降低测试脚本中定位表达式的维护成本。
1.6.3 xpath使用索引号定位
索引号表示某个被定位的页面元素在其父元素节点下的同名元素中的位置符号,需要从1开始。
示例:在被测网页中,查找第一个div标签下的“查询”按钮。
WebElement button = driver.findElement(By.xpath("//input[1]"));
代码解释:索引号定位方式是根据该页面元素在页面中相同标签名之间出现的索引位置来进行定位。上述xpath定位表达式表示查找页面中第二个出现的input元素,即被测试页面上的“查询”按钮。
备注:若在Firefox浏览器的插件中使用上述的定位方式你会发现定位到两个元素,两个div标签下的input都被定位到了,这和只查找第一个input元素相冲突,这是由于被测网页中两个div标签都包含了input标签,xpath再查找的时候把每个div节点都当作相同的起始层级开始查找,所以用//input[1]表达式会同时查找到两个div节点下的第一个input元素。如果再两div标签下还有嵌套的div,并且嵌套的div下也有input标签,也会被定位到。因此在使用索引号定位页面元素的时候,需要注意网页html代码中是否包含了多个层级完全相同的代码结构,若出现了这种情况,就需要修改定位表达式,以确保自动化测试脚本中使用的定位表达式能唯一定位所需要的元素。如果想同时定位多个相同的input页面元素可以使用下面的定位语句:
WebElement elementList = driver.findElement(By.xpath("//input[1]"));
将定位的多个元素存储到list中,然后根据list索引号获取想要的页面元素。但如果发现页面元素会经常增加或减少,就不建议使用索引号定位方式。
基于实例中的被测网页,下面给出更多的通过索引号定位的实例:
预期定位的页面元素 | 定位表达式实例 | 使用的属性值 |
---|---|---|
定位第二个div下的超链接 | //div[last()]/a | div[last()]表示最后一个div元素,last()函数获取的是指定元素的最后的索引号 |
定位第一个div中的超链接 | //div[last()-1]/a | div[last()-1]表示倒数第二个div元素 |
定位最前面一个属于div元素的子元素中的input元素 | //div/input[position()<2] | position()函数获取当前元素input的位置序号 |
1.6.4 使用页面元素的属性值定位
在定位页面元素的时候,经常会遇到各种复杂的结构的被测试网页,并且很多页面元素也没有设计ID,Name等属性,同时又不想使用绝对路径或索引号来定位页面元素,但是发现要被要被定位的页面元素拥有某些固定不变的属性及属性值,此时推荐属性定位方式来定位页面元素。
示例:定位被测网页中的第一张img元素。
WebElement img = driver.findElement(By.xpath("//input[@alt='div1-img1']"));
代码解释:表达式使用了相对路径再结合元素拥有的特定属性方法进行定位,定位元素img的属性是“alt”,值为“div1-img1”,使用@符号指明后面接的是属性,并同属性及属性值一起写到元素后的方括号中。
备注:被测试网页的元素通常会包含各种各样的属性值,并且很多属性值具有唯一性。若能确认属性值不常变并且唯一,强烈建议使用相对路径再结合属性的定位方式来编写xpath定位方式,使用此方法可以解决99%的页面元素定位问题。下面给出更多的定位实例。
预定位的页面元素 | 定位表达式实例 | 使用的属性 |
---|---|---|
定位页面的第一张图片 | //img[@href=""http://www.sogou.com] | 使用img标签的属性href值 |
定位第二个div中第一个input输入框 | //div[@id=“div2”]/input[@name=“div2input”] | 使用div变迁的name值 |
定位第二个div中第一个input输入框 | 或者//input[@name=“div2input”] | 使用input标签的name属性值 |
定位第一个div中的第一个链接 | //div[@id=“div1”]/a[@href=“http://www.sogou.com”] | 使用div标签的ID属性值。使用a标签的href属性值 |
定位页面的查询按钮 | //input[@type=“button”] | 使用input标签的type属性值 |
1.6.5 属性模糊定位
模糊属性值定位方式表示使用属性值的一部分内容定位。在自动化测试的实施过程中,常常会遇到页面元素的属性值是动态生成的,也就是说每次访问属性值都不一样,此类页面元素会加大定位难度,使用模糊属性值定位方式可以解决一部分类似难题,但前提是属性值中有一部分内容是不变的,xpath提供了一些可以实现模糊属性值的定位需求的函数。
xpath函数 | 定位表达式实例 | 表达式解释 |
---|---|---|
starts-with(str1,str2) | //img[starts-with(@alt,“div1”)] | 查找属性alt的属性值以div1关键字开始的页面元素 |
contains(str1,str2) | //img[contains(@alt,“img”)] | 查找alt属性的属性值包含img关键字的页面元素,只要包含即可,无需考虑位置 |
contains函数属于xpath的高级用法,使用场景比较多,尽管页面元素的属性值经常变化,但只要其属性值有几个固定不变的关键词,就可以使用cotains函数进行定位。
1.6.6 text()函数文本定位
示例:定位被测页面中的a链接。
WebElement text = driver.findElement(By.xpath("//a[text()='百度搜索']"));
WebElement text = driver.findElement(By.xpath("//a[contains(text(),'百度搜索')]"));
1.7 使用cssSelector定位
cssSelector定位,属于CSS高级等位,它的定位方式,利用选择器进行的。在CSS 中,选择器是一种模式,用于选择需要添加样式的对象。“CSS” 列指示该属性是在哪个CSS 版本中定义的。(CSS1、CSS2 还是CSS3。);
下面罗列了一部分的CSS定位方式,其中常用的在图中都作了标记。CSS定位是平常使用过程中非常重要的一种方式,它与xpath定位有诸多类似的地方,但是无论从性能还是语法上来说CSS都是比较有优势的:
1.一般情况下定位速度要比xpath快;
1.语法比xpath要简洁。
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
public class elementLocalization {
public static void main(String[] args) {
String chromePath=System.getProperty("user.dir") +"\\driver\\chromedriver.exe";
System.setProperty("webdriver.chrome.driver", chromePath);
WebDriver driver=new ChromeDriver();
driver.manage().window().maximize();
driver.get("https://www.baidu.com");
try {
//定义一个Web对象对象
WebElement element=null;
//CSSID定位百度搜索输入框
element=driver.findElement(By.cssSelector("#kw"));
//CSS定位所有div对象和a对象
element=driver.findElement(By.cssSelector("div,a"));
//CSS定位div标签内所有的a对象
element=driver.findElement(By.cssSelector("div a"));
//CSS定位父对象是div的所有a对象
element=driver.findElement(By.cssSelector("div>a"));
//CSS定位紧接在<div> 对象之后的所有<a> 对象
element=driver.findElement(By.cssSelector("div+a"));
//选择target="_blank" 的所有对象。
element=driver.findElement(By.cssSelector("[target='_blank']"));
//定位A标签中name='tj_trnews'并且class='mnav'的对象
element=driver.findElement(By.cssSelector("a[name='tj_trnews'][class='mnav']"));
//选择a标签对象中href 属性值以"http" 开头的每个<a> 对象。
element=driver.findElement(By.cssSelector("a[href^='http']"));
//选择a标签对象中href属性值以".com"结尾的对象
element=driver.findElement(By.cssSelector("a[href$='.com']"));
//选择a标签对象中href属性值中包含news的对象
element=driver.findElement(By.cssSelector("a[href*='news']"));
//选择属于其父对象的首个<a> 对象的每个<a> 对象
element=driver.findElement(By.cssSelector("a:nth-last-child(2)"));
//通过css绝对路径定位
element=driver.findElement(By.cssSelector("html body div#wrapper div#head div.head_wrapper div.s_form div.s_form_wrapper.soutu-env-nomac.soutu-env-index form#form.fm span.bg.s_ipt_wr.quickdelete-wrap input#kw.s_ipt"));
} catch (Exception e) {
System.out.println("定位失败");
}
}
}
2. 使用JS定位元素
以下总结了5种js定位的方法:
- 通过id获取
document.getElementById(“id”)
- 通过name获取,返回的是list
document.getElementsByName(“Name”)
- 通过标签名选取元素
document.getElementsByTagName(“tag”)
- 通过class类选取元素
document.getElementsByClassName(“class”)
兼容性:IE8及其以下版本的浏览器未实现getElementsByClassName方法
5. 通过css选择器选取元素
document.querySelectorAll(“css selector")
兼容性:IE8及其以下版本的浏览器只支持CSS2标准的选择器语法
示例代码1:
js = 'document.getElementById("helloId").click();'
driver.execute_script(js)
示例代码2:
js1 = 'document.getElementsByClassName("helloName")[0].value = "小王";' driver.execute_script(js1)
代码解释:选择元素,该元素在整个HTML文档里第一个使用CSS样式类的class= “helloName” 属性,它的value属性的值设置为“小王”。
3. 定位方法如何选择
选择策略是:选择简单、稳定的定位方法。
- 当页面元素有id属性的时候,尽量使用id来定位,没有的话,再选择其他定位方法;
- cssSelector执行速度快,推荐使用;
- 定位超链接的时候,可以考虑linkText或partialLinkText。但要注意的是,文本经常发生改变,所以不推荐使用;
- xpath功能最强悍,但是执行速度慢,因为需要查找整个DOM,所以尽量少用。实在没有办法的时候,才使用xpath。