1. 元素的定位
web⾃动化测试的操作核心是能够找到页⾯对应的元素,然后才能对元素进⾏具体的操作。 常见的元素定位方式非常多,如id,classname,tagname,xpath,cssSelector常⽤的主要由cssSelector和xpath.
1. 打开页面开发者工具:
1> fn+f12
2> ctrl+shift+i
2. 选中箭头
1.1 cssSelector
选择器的功能:选中页面中指定的标签元素
选择器的种类分为基础选择器和复合选择器,常见的元素定位方式可以通过id选择器和子类选择器来进行定位.
我们在页面选中元素后,在相关代码上点击鼠标右键,然后复制cssSelector
注意:几乎所有的搜索框的快捷键都是ctrl+f
我们按ctrl+f,把刚刚复制的选择器名称放上去进行元素查找(#表示通过id来进行定位,.表示通过class来进行定位.)
每个>表示从它的下一级子元素开始找
1.2 xpath
XML路径语言,不仅可以在XML文中查找信息,还可以在HTML中选取节点。 xpath使用路径表达式来选择xml⽂档中的节点
比如我们还是定位页面输入框,我们采用xpath形式定位: //*[@id="kw"]
xpath语法:
获取HTML页面所有的节点 //*
获取HTML页面指定的节点 //[指定节点]
举例:
//ul :获取HTML页面所有的ul节点
//input:获取HTML页面所有的input节点
获取⼀个节点中的直接子节点 /
举例:
//span/input
获取⼀个节点的父节点 ..
举例:
//input/.. 获取input节点的父节点
实现节点属性的匹配 [@...]
举例:
//*[@id='kw'] 匹配HTML页⾯中id属性为kw的节点
使用指定索引的方式获取对应的节点内容 注意:xpath的索引是从1开始的。
举例:
百度首页通过://div/ul/li[3] 定位到第三个百度热搜标签
更便捷的生成selector/xpath的方式:右键选择复制"Copy selector/xpath"
注意:元素的定位方法必须唯⼀。
具体代码实现: 注意我们自动化测试打开百度的时候是未登录状态,而我们手动打开百度是登录状态,因此我们要保持状态一致,不然我们的页面定位元素会出现错误.
这里注意:
findElement(By): 在页面查找元素,返回值是WebElement(这是个类型)
findElements(By);在页面查找元素,返回值是List<WebElement>(返回的是一组元素)
区别:
整体代码:
package org.xiaobai.seleniumautotest;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import java.util.List;
import java.net.SocketException;
public class FirstTest {
//TODO 把创建driver的操作封装成一个方法
WebDriver createDriver() throws InterruptedException {
//TODO 1. 打开浏览器 使用驱动来打开
// System.setProperty("webdriver.chrome.driver", "D:/chromedriver/chromedriver-win64/chromedriver.exe");
// 1> 使用驱动管理程序,打开对应的浏览器驱动
// WebDriverManager.chromedriver().setup();
//增加浏览器配置对象,创建驱动对象的时候要强制运行访问所有的链接
ChromeOptions options = new ChromeOptions();
//表示运行访问所有的链接
options.addArguments("--remote-allow-origins=*");
// 2> 创建一个浏览器驱动对象
WebDriver driver = new ChromeDriver(options);
Thread.sleep(3000);
return driver;
}
//测试百度搜索关键词: 李白
void test01() throws InterruptedException, SocketException {
WebDriver driver = createDriver();
//TODO 2. 输入完整网址: https://www.baidu.com
//把网址发给浏览器驱动对象
driver.get("https://www.baidu.com");
Thread.sleep(3000);
//TODO 3. 找到输入框,并输入关键词: 李白
//找到前端的输入框,并输入关键词
driver.findElement(By.cssSelector("#kw")).sendKeys("李白");
Thread.sleep(3000);
//TODO 4. 找到百度一下按钮,并进行点击
driver.findElement(By.cssSelector("#su")).click();
Thread.sleep(3000);
//TODO 5. 关闭浏览器
// 就是关闭浏览器对象
driver.quit();
}
//TODO 元素的定位
void test02() throws InterruptedException {
//创建驱动,进入百度页面
WebDriver driver = createDriver();
driver.get("https://www.baidu.com");
Thread.sleep(3000);
//通过选择器:非登录状态下定位到的元素
// driver.findElement(By.cssSelector("#s-hotsearch-wrapper > div > a.hot-title > div"));
// driver.quit();
//通过xpath来定位
// driver.findElement(By.xpath("//*[@id=\"kw\"]"));
//返回一组元素
List<WebElement> elements = driver.findElements(By.cssSelector("#hotsearch-content-wrapper > li > a > span.title-content-title"));
for(int i = 0;i<elements.size();i++){
//获取ele文本信息
System.out.println(elements.get(i).getText());
}
driver.quit();
}
}
2. 操作测试对象
2.1 点击/提交对象
click()方法
举例; 找到百度⼀下按钮并点击
driver.findElement(By.cssSelector("#su")).click();
注意: 除了按钮之外,页面的元素基本上都是可以点击的,但是页面隐藏的标签,不可见的标签就不能点击(看关键词是否有hidden,每个标签对应的样式)
2.2 模拟按键输⼊
sendKeys("")方法(可以输入任何东西),输入框可以接收的内容都可以通过sendKeys发送过去
driver.findElement(By.cssSelector("#kw")).sendKeys("输入文字");
2.3 清除文本信息
输⼊⽂本后⼜想换⼀个新的关键词(想要在一个场景下更换多个关键词,需要把前一个关键词清除掉,如果不清除,每次sendKeys将会完成拼接操作),这⾥就需要⽤到 clear()
driver.findElement(By.cssSelector("#kw")).sendKeys("李白");
driver.findElement(By.cssSelector("#kw")).clear(); driver.findElement(By.cssSelector("#kw")).sendKeys("李小白");
2.4 获取文本信息
如果判断获取到的元素对应的⽂本是否符合预期呢?获取元素对应的⽂本并打印⼀下.
获取文本信息: getText()
String bdtext = driver.findElement(By.xpath("//*[@id="title-content"]/span[1]")).getText();
System.out.println("打印的内容是:"+bdtext);
但是当我们获得按钮上的文本我们发现了问题,打印的按钮文字是空
原因是按钮里面的文字百度一下是按钮(input)的属性值,而刚刚获取的文本是用span标签包裹的文字
因为是属性值,所以我们需要使用getAttribute(String)来获取属性值
2.5 获取当前页面标题
url和标题是无法通过前端来定位到的,我们需要使用特定的方法来进行定位
getTitle()
2.6 获取当前页面URL
getCurrentUrl()
应用场景: 当我们以后跳转到其他页面的时候,新的页面的标题和url是不同的,通过打印url和标题我们就可以知道我们打开的页面是不是我想要的页面(进行页面的测试)
3.窗口
什么是窗口,这个就是一个窗口,我们可以对窗口大小进行设置,切换窗口,截屏等操作
3.1 设置窗口大小
window()是Options的一种
window()的函数源码介绍
//窗口最大化
driver.manage().window().maximize();
//窗⼝最小化
driver.manage().window().minimize();
//全屏窗口
driver.manage().window().fullscreen();
//⼿动设置窗口大小
driver.manage().window().setSize(new Dimension(1024, 768));
3.2 切换窗口
问题的引入: 我们跳转后的页面,用的驱动还是作用于原先的页面,因此找不到跳转后页面的元素 .
1)获取当前⻚⾯句柄:
driver.getWindowHandle();
2)获取所有⻚⾯句柄
driver.getWindowHandles();
3)切换当前句柄为最新⻚⾯:dricer.switchTo.window();切换句柄
String curWindow = driver.getWindowHandle();
Set<String> allWindow = driver.getWindowHandles();
for (String w : allWindow) {
if (w != curWindow) {
driver.switchTo().window(w);
}
}
刚刚问题的解决方法: 首先我们要知道句柄和driver的关系
句柄是浏览器窗口或者标签页的唯一标识符,通常是一个字符串(每个窗口或者标签页都有唯一的一个句柄,句柄的作用是帮助driver在多个窗口或者标签页进行切换.
driver是控制浏览器实例的对象,它控制的是当前活动的窗口或者标签页.
因此我们切换页面就要使用句柄来进行切换
关于driver在更多页面进行跳转的问题(我们也可以每次打开一个页面就获取所有的句柄然后保存并且切换为新的句柄)解决方式
注意:执⾏了driver.close()之前需要切换到未被关闭的窗⼝
3.3 屏幕截图
我们的⾃动化脚本⼀般部署在机器上⾃动的去运⾏,如果出现了报错,我们是不知道的,可以通过抓拍来记录当时的错误场景
屏幕截图⽅法需要额外导⼊包:
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
File file = ((TakesScreenshot)webDriver).getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(file,new File(filename));
但是此时生成的图片有个问题: 每次运行都会覆盖上一次的截图,我们要求每次生成的图片都统一放在一个文件夹下,并且生成的屏幕截图名称就晓得是哪个测试的.
我们根据那一天,哪个时间,哪个方法来进行文件的分类,
具体代码:
public class FirstTest {
WebDriver driver = null;
//TODO 把创建driver的操作封装成一个方法
void createDriver() throws InterruptedException {
//增加浏览器配置对象,创建驱动对象的时候要强制运行访问所有的链接
ChromeOptions options = new ChromeOptions();
//表示运行访问所有的链接
options.addArguments("--remote-allow-origins=*");
// 2> 创建一个浏览器驱动对象
driver = new ChromeDriver(options);
Thread.sleep(3000);
}
void test05() throws InterruptedException, IOException{
//创建浏览器驱动对象
createDriver();
Thread.sleep(1000);
driver.get("https://www.baidu.com");
Thread.sleep(1000);
//切换页面前进行截图
getScreenShot(getClass().getName());//获取当前类的方法名称
Thread.sleep(1000);//记得要加时间缓存,因为截图需要时间
System.out.println("转换句柄之前: "+driver.getTitle());
//点击新闻
Thread.sleep(1000);
driver.findElement(By.cssSelector("#s-top-left > a:nth-child(1)")).click();
Thread.sleep(1000);
//切换driver指向的句柄
//1.获取当前页面的句柄
String curHandle = driver.getWindowHandle();
System.out.println("切换前的句柄: "+curHandle);
//2.获得所有页面的句柄
Set<String> allHandles = driver.getWindowHandles();
for (String handle : allHandles){
if(handle != curHandle){
//切换driver
driver.switchTo().window(handle);
}
}
//打印切换后的driver指向的句柄,这样才能找到我们要的元素
System.out.println("切换后的句柄: "+driver.getWindowHandle());
driver.findElement(By.cssSelector("#headline-tabs > ul > li"));
getScreenShot(getClass().getName());//获取当前类的方法名称
Thread.sleep(2000);
System.out.println("转换句柄之后: "+driver.getTitle());
driver.quit();
}
//TODO 进阶版本的屏幕截图
void getScreenShot(String str) throws InterruptedException, IOException {
//保存的图片路径: ./src/test/image/
// /2024-08-17/
// /test01-17432.png
// /test02-17432.png
// /test03-17432.png
// /2024-08-18/
// /test01-17432.png
// /test02-17432.png
// /test03-17432.png
//
// createDriver();
//屏幕截图
//设计文件日期格式
SimpleDateFormat sim1 = new SimpleDateFormat("yyyy-MM-dd");//参数是年月日
SimpleDateFormat sim2 = new SimpleDateFormat("HHmmssSS");//参数时分秒毫秒
//生成当前的时间
String dirTime = sim1.format(System.currentTimeMillis());
String fileTime = sim2.format(System.currentTimeMillis());
//拼接文件路径
//./src/test/image/2024-08-17/test01-17432.png
String filename = "./src/test/image/"+dirTime+"/"+str + "-" + fileTime + ".png";//方法名-时间.png
File srcFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
//把srcFile放到指定位置
FileUtils.copyFile(srcFile,new File(filename));
}
}
此时要注意,我们每次创建浏览器驱动指向的百度页面的句柄都是不一样的,因此如果我们在截图方法中也创建驱动,就形成了新的句柄,我们在这个句柄上就找不到原来句柄上的元素了.
然后就是我们拼接文件路径格式的时候,我们需要精确到毫秒级别,因为自动化测试的速度很快,精确到毫秒截图才有区分度
3.4关闭窗口
driver.close();// 关闭窗口而非整个浏览器(关闭当前的标签页)
driver.quit();//这个函数是把整个driver给关闭掉(关闭浏览器,释放driver对象)
注意:窗⼝关闭后driver要重新定义
如果有俩个页面,我们连续关闭俩次会报错:因为我们跳转后driver指向的是跳转后的页面,在close之后,我们driver指向的句柄不是跳转前的句柄了,因此我们还是得切换句柄,然后我们在进行关闭,关闭俩次后页面虽然没了,但是我们需要释放driver,调用close方法
解决方案
应用场景: 测试打开的新的标签页之后要继续返回到前一个标签页中进行测试
4. 等待
出现NoSuchElement的解决方法
问题的引入
通常代码执⾏的速度比页面渲染的速度要快,如果避免因为渲染过慢出现的⾃动化误报的问题呢?可以使⽤selenium中提供的三种等待⽅法:强制等待,隐式等待,显示等待
4.1 强制等待
Thread.sleep(秒)(以阻塞线程的形式来让程序进行等待)
优点:使⽤简单,调试的时候⽐较有效.可以直接阻塞程序
缺点:影响运⾏效率,极大的增加了自动化的执行时间.(假设1000条测试用例,每个用例添加强制平均时间为3s,那么总的时间为3000s=50min,基本上是天文数字,理想时间是<10分钟,最佳时间2~3分钟)
4.2 隐式等待
隐式等待是⼀种智能等待,他可以规定在查找元素时,在指定时间内不断查找元素。如果找到则代码继续执⾏,直到超时没找到元素才会报错。(比如元素没有找到NoSuchElement),比较的有弹性
隐式等待和强制等待的区别: 强制等待规定等多久就必须等多久(死等),隐式等待如果在规定时间之前就找到元素就不用继续等了(只能等待).
implicitlyWait() 参数:Duration类中提供的毫秒、秒、分钟等⽅法
在3秒内找到元素就继续执行,在3秒内没有找到元素,就报错(NoSuchElement),在隐式等待中会一直轮循寻找元素, 寻找到了就继续执行下面的代码(是每个元素都会去在3秒内先查找是不是存在这个元素)
内部调用栈
隐式等待作用域是整个脚本的所有元素。即只要driver对象没有被释放掉(driver.quit()),隐式等待就⼀直生效。
优点:智能等待,作用于全局(只要创建了驱动对象开始,只要没有quit掉,后续所有的代码都是生效的)
缺点: 只能查找元素,每次查找元素都要等待(只要findElemet都要等一下,看有没有元素)
但是有个问题:
在这个代码,先搜索迪丽热巴,再搜索邓紫棋,输出邓紫棋的名字,结果输出的是迪丽热巴而不是邓紫棋
原因: implicitWait只用来查找元素是否存在,不检查元素对应的内容(只保证元素能不能找到,具体执行什么内容是不管的),也就是在执行到最后一个打印名字的函数的时候,前面的sendKeys,click等操作都还没有执行完,此时ele里面的元素还是之前的元素(隐式等待只关心有没有这个元素,至于是不是就不关心),因此打印出来的名字是迪丽热巴不是邓紫棋.
解决办法:
在查找之前我们加一个强制等待,前面的sendKeys,click+页面渲染等操作就可以执行完了
4.3 显示等待
显示等待也是⼀种智能等待,在指定超时时间范围内只要满足操作的条件(元素在不在,可不可以点击,看一下文本在不在,文本有没有包含我要的前缀或者后缀....都可以设置)就会继续执⾏后续代码
ExpectedConditions预定义⽅法的⼀些⽰例:
• elementToBeClickable(By locator) ‒ 元素是否可点击
• textToBe(Bylocator,String str) - 元素是否符合预期(精确匹配)
• textToBePresentInElement() - 模糊匹配
• presenceOfElementLocated(Bylocator) ‒ 检查页面的 DOM 上是否存在元素。
• urlToBe(java.lang.String url) ‒ 检查当前页面的 URL 是⼀个特定的 URL。
强制等待只会作用在指定的元素上(后续要使用什么条件直接去找即可)
若在时间内没有满足条件会报错
优点:显⽰等待是智能等待,可以⾃定义显⽰等待的条件,操作灵活
缺点:写法复杂
代码演示:
显示等待和隐式等待一起使用会不会有问题?
我们发现并没有等待10+5=15秒,而此刻我们的时间是4秒,多运行几次发现时间都是不同的,因此得出一个结论: 混合使用隐式和显示等待,可能会导致不可预测的等待时间.
5. 浏览器导航
常见操作:
1)打开⽹站
// 更⻓的⽅法
driver.navigate().to("https://selenium.dev");
// 简洁的⽅法
driver.get("https://selenium.dev");
2)浏览器的前进、后退、刷新
driver.navigate().back();
driver.navigate().forward();
driver.navigate().refresh();
一般浏览器页面上的前进后退和刷新操作是无法定位到的,我们需要selenium里面提供的方法
模拟实现前进后退刷新
6. 弹窗
弹窗是在页面是找不到任何元素的,这种情况怎么处理?使⽤selenium提供的Alert接⼝
弹窗分为三种: 警告弹窗,确认弹窗,提示弹窗
弹窗的操作有三种
在进行这三种操作之前我们都需要切换弹窗,driver才能作用到弹窗上面
1> 点击确认
2> 点击取消
3> 输入文本信息
6.1 警告弹窗+确认弹窗
Alert alert = driver.switchTo.alert(); //确认
alert.accept()
//取消
alert.dismiss()
但是现在来看这个,我们没有点击确认然后再次查找元素
就会报错:当前页面有弹窗未处理,无法继续执行页面元素相关的操作
取消操作
6.2 提示弹窗
Alert alert = driver.switchTo.alert();
alert.sendKeys("hello");(往弹窗输入内容)
alert.accept(); alert.dismiss();
7. 文件上传
点击⽂件上传的场景下会弹窗系统窗口,进⾏⽂件的选择。selenium