一:Selenium简介
Selenium用于Web应用程序测试的工具,开源并且免费的,覆盖IE、Chrome、FireFox、Safari等主流浏览器,通过在不同浏览器中运行自动化测试。支持Java、Python、Net、Perl等编程语言进行自动化测试脚本编写。
Selenium家族:
- Selenium IDE,Firefox/Chrome浏览器的扩展插件,通过Selenium IDE我们可以录制和回放浏览器操作,快速实现自动化测试,功能很鸡肋,很难扩展自定义功能,一般很少用。
- Selenium WebDriver,Selenium的核心,提供了各种语言环境的API来支持更多控制权和编写符合标准软件开发实践的应用程序。
- Selenium Grid,分布式测试,通过Selenium Grid可以将自动化测试脚本分发到不同的测试机器中执行。
二:驱动的使用:
1、浏览器驱动下载地址:
2、设置驱动文件路径:
(1)Chrome驱动:System.setProperty("webdriver.chrome.driver","驱动路径")
(2)Firefox驱动:System.setProperty("webdriver.firefox.driver","驱动路径")
(3)IE驱动:IE驱动受到的限制比较多,一般需要3个步骤即可解决常见的报错问题,少一步都可能会报错:1、取消IE安全设置 2、忽略浏览器缩放设置 3、设置驱动路径
DesiredCapabilities capabilities = new DesiredCapabilities();
//取消IE安全设置(忽略IE的Protected Mode的设置)
capabilities.setCapability(InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS, true);
//忽略浏览器缩放设置
capabilities.setCapability(InternetExplorerDriver.IGNORE_ZOOM_SETTING, true);
System.setProperty("webdriver.ie.driver", "src\\test\\resources\\IEDriverServer.exe");
WebDriver webDriver = new InternetExplorerDriver(capabilities);
3、驱动的执行原理:
三:元素定位
有八大元素定位方式:id、name、tagName、className、linkText、 partialLinkText 、 cssSelector 、 xpath
1、ID定位
如果页面存在ID唯一定位到的元素,可以优先考虑使用id定位,如:百度的输入框定位:By.id("kw")
2、name定位
根据元素的name属性定位。如上述百度输入框可以使用name定位:By.name("wd")
3、tagName定位:
根据元素的标签名定位。该定位元素很少使用,因为页面中可能会存在很多相同标签名的元素,这时候就无法唯一定位到自己想要操作的元素,比如上述百度输入框的元素标签是input标签,由于页面存在很多input元素,所以无法唯一定位到我们想要的元素
4、className定位
根据样式名定位。如果元素存在class属性,根据class属性可以唯一定位到该元素可以考虑使用该种定位方式:如上述百度输入框可以使用className定位:By.className("s_ipt")
5、linkText定位
根据超链接完整文本定位。该定位方式只用于定位超链接标签元素,比如:百度页面的“新闻”超链接可以使用该种定位方式:By.linkText("新闻")
6、partialLinkText 定位
超链接部分文本匹配定位。该定位方式也是只能用于定位超链接标签的元素,由于某些链接的文本很长,我们可以取代表性文本唯一定位,如:百度页面的"hao123"超链接可以使用该种定位方式:By.partialLinkText("hao")
7、cssSelector定位
根据CSS选择器定位,若用id定位,则用 #。若用class定位,则用.
该种定位方式有六种,以下用百度输入框举例:
(1)根据元素id定位:By.cssSelector("#kw")
(2)根据元素class定位:By.cssSelector(".s_ipt")
(3)根据元素标签名定位:跟上述的tagName定位类似,由于页面可能会存在很多相同名字标签,无法做到唯一定位,一般都不用该方式
(4)根据元素单属性定位:By.cssSelector("input[id='kw']"),By.cssSelector("input[name='wd']"),By.cssSelector("input[class='s_ipt']")
(5)根据元素多属性定位:By.cssSelector("input[id='kw'][name='wd']")
(6)根据元素层级定位:格式:父标签[父标签属性名=父标签属性值]>子标签,如此处的百度输入框定位可以写成:By.cssSelector("span[class='bg s_ipt_wr new-pmd quickdelete-wrap']>input")
8、XPATH定位
本文最为喜欢使用的定位方式,可以解决绝大部分的元素定位,特别推荐使用。XPATH有绝对定位、相对定位、轴定位三大种。
(1)绝对定位:根据页面的根元素开始,一直不断取到自己想定位的元素,如果不是万分无奈,都不要用此种定位方式,因为页面中如果某个元素发生了变化,定位表达式可能就会失效,而且很难排查是哪个元素发生了改变。
(2)相对定位:非常推荐使用。以//表示从页面的任意指定符合的元素节点开始进行解析,而/表示直接的下级元素,*表示通配任意的元素。使用方式:
(2.1)根据属性定位:By.xpath("//标签名[@属性名='属性值']"),如:百度输入框:By.xpath("//input[@id='kw']"),By.xpath("//*[@id='kw']")
(2.2)根据文本定位:By.xpath("//标签名(text()='值')"),如:百度页面的新闻:By.xpath("//a[text()='新闻']"),By.xpath("//*[text()='新闻']")
(2.3)根据属性值模糊匹配:By.xpath("//标签名[contains(@属性名,'值')]"), 如百度页面的新闻:By.xpath("//a[contains(@href,'news.baidu')]")
(2.4)根据文本值模糊匹配:By.xpath("//标签名[contains(text(),'值')]),如百度页面的hao123:By.xpath("//a[contains(text(),'hao123')]")
(2.5)根据层级关系定位:使用场景:页面元素可能无法通过常规定位方法定位,可以考虑父元素或者祖先元素是否有可以唯一定位的元素,通过该唯一定位元素再去定位我们想要的元素。
(2.5.1)定位直接父元素再定位我们想要的元素://*[@id='父元素唯一id']/child
(2.5.2)定位祖先元素再定位我们想要的元素://*[@id='祖先元素唯一id']//child
(3)轴定位:相当于Xpath的必杀器了,基本可以解决95%的定位元素疑难杂症,上面所有的定位方式都无法定位元素的时候,可以考虑使用该定位。
轴名称 | 释义 |
---|---|
ancestor | 选取当前节点的所有祖先节点(包括父节点) |
parent | 选取当前节点的父节点 |
preceding | 选取当前节点之前的所有节点 |
preceding-sibling | 选取当前节点之前的所有兄弟节点 |
following | 选取当前节点之后的所有节点 |
following-sibling | 选取当前节点之后的所有兄弟节点 |
使用方式:轴名称::标签名
示例:取百度搜索框的父元素span标签元素: //input[@id='kw']//parent::span
取百度搜索框之前的兄弟元素span标签元素://input[@id='kw']//preceding::span[@class='soutu-btn']
四:selenium常用操作 API
1、元素对象常用API
首先先有元素对象:Webelement element=driver.findElement(By by);其中by是元素定位信息
(1.1)、点击操作:element.click();
(1.2)、输入内容:element.sendKeys("str");
(1.3)、清除内容:element.clear();
(1.4)、键盘操作:
(1.4.1)element.sendKeys(Keys.CONTROL,"a");//ctrl+a 全选
(1.4.2)element.sendKeys(Keys.CONTROL,"x");//ctrl+x 剪切
(1.4.3)element.sendKeys(Keys.CONTROL,"c");//ctrl+c 复制
(1.4.4)element.sendKeys(Keys.CONTROL,"v");//ctrl+v 粘贴
(1.4.5)element.sendKeys(Keys.ENTER);//回车
(1.4.6)element.sendKeys(Keys.BACK_SPACE);//删除
(1.4.7)element.sendKeys(Keys.SPACE);//空格键
(1.5)、获取元素标签名:element.getTagName();
(1.6)、获取元素属性:element.getAttribute();
(1.7)、获取元素文本值:element.getText();
2、driver对象常用API
(2.1)、访问指定url:driver.get("url")
(2.2)、获取当前页面url:driver.getCurrentUrl();
(2.3)、获取当前页面标题:driver.getTittle();
(2.4)、获取当前页面源码:driver.getPageSource();
(2.5)、关闭driver当前所在窗口:driver.close();
(2.6)、关闭driver对象以及所有窗口:driver.quit()
(2.7)、窗口操作:
首先先获取window对象:Window window = driver.manage().window();
(2.7.1)、窗口最大化:window.maximize();
(2.7.2)、浏览器全屏:window.fullscreen();
(2.7.3)、获取窗口位置:window.getPosition();
(2.7.4)、获取窗口大小:window.getSize();
(2.7.5)、设置窗口位置:window.setPosition(targetPosition)
(2.7.6)、设置窗口大小:window.setSize(targetSize);
(2.8)、截屏操作:需要导入依赖包:Commons IO
File file=driver.getScreenshotAs(OutputType FILE);
try{
FileUtils.copyFile(file,new File("E:\\projectDir"));
}catch(IOException e){
e.printStackTrace();
}
五:三种等待:硬等待、隐式等待、显式等待
1、硬等待:Thread.sleep(long millis)
特点:强制线程等待
优点:使用简单
缺点:容易造成时间浪费,建议少用
2、隐式等待:driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
特点:在设置的超时时间范围内不断查找元素,直到找到元素或者超时为止
优点:只需要设置一次,在 WebDriver 实例的整个生命周期都是生效的
缺点:只能找到元素,不能适用条件更复杂的情况,如:元素可点击、元素可见、元素的属性发生变化等。
3、显示等待:
特点:可以灵活指定等待到元素满足某个条件为止,最常用
优点:基本可以满足所有元素等待条件,使得自动化脚本更加稳定
缺点:暂无缺点
使用方法:先创建一个WebDriverWait对象: WebDriverWait webDriverwait=new WebDriverWait();
//等待元素可点击
webDriverWait.until(ExpectedConditions.elementToBeClickable(By));
//等待元素可见,光是找到元素不行,必须得能被看到
webDriverWait.until(ExpectedConditions.visibilityOfElementLocated(By));
//等待元素找到
webDriverWait.until(ExpectedConditions.presenceOfElementLocated(By));
//等待所有元素可见
webDriverWait.until(ExpectedConditions.visibilityOfAllElementsLocatedBy(By));
(1)封装等待元素可见方法:
/**
* 显式等待元素可见二次封装
* @param driver 驱动对象
* @param by 元素定位信息
*/
public WebElement waitElementVisible(RemoteWebDriver driver, By by ){
WebElement webElement = null;
try {
//1、实例化WebDriverWait 超时时间10s
WebDriverWait webDriverWait = new WebDriverWait(driver,10);
//2、通过until方法等到某个条件满足时为止
webElement = webDriverWait.until(ExpectedConditions.visibilityOfElementLocated(by));
}catch (Exception e){
e.printStackTrace();
}
return webElement;
}
(2)封装元素可被点击方法:
/**
* 显式等待元素可被点击二次封装
* @param driver 驱动对象
* @param by 元素定位信息
*/
public WebElement waitElementClickable(RemoteWebDriver driver, By by ){
WebElement webElement =null;
try {
//1、实例化WebDriverWait 超时时间10s
WebDriverWait webDriverWait = new WebDriverWait(driver, 10);
//2、通过until方法等到某个条件满足时为止
webElement = webDriverWait.until(ExpectedConditions.elementToBeClickable(by));
}catch (Exception e){
e.printStackTrace();
}
return webElement;
}
六:三种切换
1、切换windows
当我们点击a标签元素时,如果a标签有target="_blank"
时,就会打开一个新的窗口,这时候我们如果想要操作新窗口的元素,需要切换windows。
/**
* 封装的通用切换窗口的方法-根据对应窗口的标题来切换
* @param title 窗口标题
*/
public void switchWindowWithTitle(String title){
Set<String> allWindowHandles = driver.getWindowHandles();
for (String windowHandle: allWindowHandles){
//根据窗口的标题来进行判断
if(title.equals(driver.getTitle())){
break;
}else {
logger.info("切换到标题为:【"+title+"】的窗口");
driver.switchTo().window(windowHandle);
}
}
}
/**
* 封装的通用切换窗口的方法-根据对应窗口的url来切换
* @param url 窗口url
*/
public void switchWindowWithURL(String url){
Set<String> allWindowHandles = driver.getWindowHandles();
for (String windowHandle: allWindowHandles){
//根据窗口的URL来进行判断
if (url.equals(driver.getCurrentUrl())){
break;
}else {
logger.info("切换到url为:【"+url+"】的窗口");
driver.switchTo().window(windowHandle);
}
}
}
2、切换Iframe
当想要定位iframe中的元素时,我们需要切换Iframe,否则无法定位Iframe里的元素
/**
* 切换到指定IFrame封装
* @param driver 驱动对象
* @param by 元素定位信息
* @param frameInfo 自定义frame信息
*/
public void switchFrame(RemoteWebDriver driver,By by,String frameInfo){
WebElement element = waitElementVisible(driver, by);
logger.info("切换IFrame:"+frameInfo);
driver.switchTo().frame(element);
}
/**
* 从IFrame中切换到默认页面封装
* @param driver 驱动对象
*/
public void switchDefaultFrame(RemoteWebDriver driver){
logger.info("切换回默认的页面");
driver.switchTo().defaultContent();
}
3、切换Alert弹窗
有些时候,由浏览器弹出一个Alert提示框,如果我们不去处理,无法进行下一步操作。
/**
* Alert弹窗切换
* @param driver 驱动对象
*/
public void switchAlert(RemoteWebDriver driver){
logger.info("切换到alert窗口");
Alert alert = driver.switchTo().alert();
// alert.accept(); //点击确定
//alert.dismiss(); //点击取消
alert.getText(); //获取弹窗文本
}
七:特殊元素操作
1、JavaScript操作
某些特殊情况下,使用selenium的api无法操作页面元素,或者运行的时候报元素无法被点击等异常错误时,这时候可以考虑通过JavaScript操作是否能解决问题。
使用方式:
(1)不传参方式:
driver.executeScript("JS代码");
(2)传参方式:
WebElement element = driver.findElement(By.id("xx"));
driver.executeScript("arguments[0].click();",element);
使用场景:(1)设置/去除元素属性:setAttribute()/removeAttribute(),这里以12306网站为例:
driver.get("https://www.12306.cn/index/");
Thread.sleep(2000);
//设置元素的属性值
driver.executeScript("document.getElementById('train_date').setAttribute('value','2020-1-1');");
//移除掉元素的属性值
driver.executeScript("document.getElementById('train_date').removeAttribute('value');");
(2)页面滚动
实例1:12306网站操作:
driver.get("https://www.12306.cn/index/");
Thread.sleep(2000);
//滚动到页面底部
//driver.executeScript("window.scrollTo(0, document.body.scrollHeight);");
//2-2、滚动到指定的元素上去
driver.executeScript("document.getElementById('index_ads').scrollIntoViewIfNeeded(true);");
实例2:在豌豆荚软件排行页面"https://www.wandoujia.com/top/app"不断地滚动对【虎牙直播】元素点击。
ublic class UITest04 {
public static void main(String[] args) throws InterruptedException {
RemoteWebDriver driver = getDriver("chrome");
driver.get("https://www.wandoujia.com/top/app");
driver.manage().window().maximize();
Thread.sleep(1000);
while (true){
if (driver.getPageSource().contains("title=\"虎牙直播\"")) {
driver.findElementByXPath("//a[text()='虎牙直播']").click();
break;
}else {
//点击查看更多
WebElement loadMore = waitElementVisible(driver, By.xpath("//div[@class='load-more']/a"));
//判断【查看更多】是否已经加载完,如果加载完,则跳出循环
if (loadMore.getAttribute("style").equals("display: none;")){
break;
}
driver.executeScript("arguments[0].scrollIntoViewIfNeeded(true);", loadMore);
loadMore.click();
Thread.sleep(1000);
}
}
Thread.sleep(1000);
driver.quit();
}
/**
* 封装的通用切换窗口的方法-根据对应窗口的标题来切换
* @param driver 驱动对象
* @param title 窗口标题
*/
public static void switchWindow(WebDriver driver, String title) {
Set<String> allWindowHandles = driver.getWindowHandles();
for (String windowHandle: allWindowHandles){
//根据窗口的URL地址或者标题来进行判断
if(title.equals(driver.getTitle())){
break;
}else {
driver.switchTo().window(windowHandle);
}
}
}
/**
* 显式等待元素可见二次封装
* @param driver
* @param by
*/
public static WebElement waitElementVisible(WebDriver driver, By by ){
//1、实例化WebDriverWait 超时时间10s
WebDriverWait webDriverWait = new WebDriverWait(driver,10);
//2、通过until方法等到某个条件满足时为止
WebElement webElement = webDriverWait.until(ExpectedConditions.visibilityOfElementLocated(by));
return webElement;
}
/**
* 显式等待元素可被点击二次封装
* @param driver
* @param by
*/
public static WebElement waitElementClickable(WebDriver driver, By by ){
//1、实例化WebDriverWait 超时时间10s
WebDriverWait webDriverWait = new WebDriverWait(driver,10);
//2、通过until方法等到某个条件满足时为止
WebElement webElement = webDriverWait.until(ExpectedConditions.elementToBeClickable(by));
return webElement;
}
/**
* 打开所有浏览器封装
* @param type 浏览器类型
* @return
*/
public static RemoteWebDriver getDriver(String type){
RemoteWebDriver driver = null;
if("chrome".equals(type)){
//1、加载浏览器驱动
System.setProperty("webdriver.chrome.driver","src/test/resources/chromedriver.exe");
//2、创建ChromeDriver对象
driver =new ChromeDriver();
}else if ("firefox".equals(type)){
//加载浏览器驱动
System.setProperty("webdriver.gecko.driver","src/test/resources/geckodriver.exe");
//如果firefox不是默认路径,配置firefox路径
System.setProperty("webdriver.firefox.bin","D:\\Program Files\\Mozilla Firefox\\firefox.exe");
driver=new FirefoxDriver();
}else if ("ie".equals(type)){
//加载浏览器驱动
System.setProperty("webdriver.ie.driver","src/test/resources/IEDriverServer.exe");
//IE浏览器默认会有125%的缩放和安全设置,需要禁用,不然会报错
DesiredCapabilities capabilities=new DesiredCapabilities();
capabilities.setCapability(InternetExplorerDriver.IGNORE_ZOOM_SETTING,true); //忽略浏览器缩放比例
capabilities.setCapability(InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS,true);//忽略IE浏览器的安全模式
//创建IEDriver
driver=new InternetExplorerDriver();
}else {
System.out.println("浏览器传值有误");
}
return driver;
}
}
(3)执行的时候,控制台报错如下:Element is not clickable at point,Other element would receive the click ,或者其他奇形怪状的错误的时候,都可以考虑使用JavaScript操作尝试下是否能解决错误。
2、鼠标操作
自动化有些场景是需要鼠标配合使用的时候,可以使用Selenium的Actions类来模拟鼠标操作,通过Actions对象可以发起鼠标左键、右键、移动鼠标等操作,最后使用perform方法执行操作。
clickAndHold(element) //在特定元素上单击鼠标左键(不释放)
release(element) //在特定元素上释放鼠标左键
doubleClick(element) //在特定元素上双击鼠标左键
moveToElement(element) //移动鼠标指针到特定元素
contextClick(element) //在特定元素上右键单击
dragAndDrop(element) //拖拽元素
perform() //执行具体的操作,前面6个方法都是声明一个操作,只有调用perform()后才会真正执行操作
使用方式:
Actions actions = new Actions(driver);
WebElement webElement1 = driver.findElement(By.id("XX"));
WebElement webElement2 = driver.findElement(By.id("XX"));
//按住-->拖拽-->鼠标释放
actions.clickAndHold(webElement1).moveToElement(webElement2).release().perform();
//双击
actions.doubleClick(webElement1).perform();
3、文件上传操作
4:验证码操作
(1)、与开发沟通在测试环境去除验证码
(2)、在测试环境开个万能验证码,推荐使用
(3)、使用自动识别技术破解验证码,时间充裕可以尝试
(4)、通过读取Redis缓存,绕过验证码。