介绍
Selenium是最广泛的web端UI自动化框架,支持java、python、ruby等编辑语言。 官网地址:https://selenium.dev/
Selenium家族
- Selenium IDE: 是浏览器的扩展插件,通过Selenium IDE我们可以录制和回放浏览器操作。
- Selenium WebDriver: Selenium的核心,提供了各种语言环境的API来支持更多控制权和编写符合标准软件开发实践的应用程序。
- Selenium Grid: 分布式测试,通过Selenium Grid可以将自动化测试脚本分发到不同的测试机器中执行。
Selenium WebDriver
一、引入Maven依赖
,
<!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.141.59</version>
</dependency>
****仓库地址:****https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java
下载浏览器驱动(版本须于浏览器一致)
- 在chrome浏览器的地址栏上面输入chrome://version获取chrome版本/在chrome设置中查看对应版本
二、下载对应的chromedriver驱动:
-
谷歌版本122驱动百度云盘获取 链接:https://pan.baidu.com/s/1VfCzRFFcJh4sGQhVUOWZEg
提取码:1234 -
其他版本驱动:https://chromedriver.storage.googleapis.com/index.html
将驱动解压至项目目录下
//引用driver路径(路径为相对路径)
System.setProperty("webdriver.chrome.driver", "src/test/resources/chromedriver-win32/chromedriver.exe");
//引用浏览器driver
ChromeDriver chromeDriver = new ChromeDriver();
//访问URL
chromeDriver.get("https://www.qimao.com/");
基础元素定位
id定位:
By.id("id");
name定位
By.name("名称");
标签名定位
By.tagName("标签名");
样式名定位
By.className("样式名");
超链接完整文本定位
By.linkText("超链接完整文本地址");
超链接部分文本定位
By.partialLinkText("超链接部分文本地址");
css选择器定位
By.cssSelector("标签名") //实现方式可百度
xpath定位
- xpath其实就是一个path(路径),一个描述页面元素位置信息的路径,相当于元素的坐标
绝对定位(禁止使用,后期维护成本极差)
chromeDriver.findElement(By.xpath("/html/body/div[1]/div/div/header/div[2]/div/div/div[2]/div[2]/div/a/span");
相对定位 相对路径以//表示
//通过文本定位
chromeDriver.findElement(By.xpath("//span[text()=\"立即登录\"]"));
//xxx为属性名, yyy为对应的值
chromeDriver.findElement(By.xpath("//span[@xxx=\"yyy\"]"));
//模糊匹配
chromeDriver.findElement(By.xpath("//span[contains(text(),\"立即\")]"));
xpath轴定位
ancestor | 选取当前结点的所有先辈(父、祖父等) |
---|---|
ancestor-or-self | 选取当前节点的所有先辈(父、祖父等)以及当前节点本身 |
attribute | 选取当前节点的所有属性。@id等价于attribute::id |
child | 选取当前节点的所有子元素,title等价于child:title |
descendant | 选取当前节点的所有后代元素(子、孙等) |
descendant-or-self | 选取当前节点的所有后代运算(子、孙等)以及当前节点本身 |
following | 选取文档中当前节点的结束标签之后的所有结点 |
namespace | 选取当前节点的所有命名空间节点 |
parent | 选取当前节点的父节点 |
preceding | 选取当前节点的父节点 |
preceding-sibling | 选取当前节点之前的所有同级节点 |
self | 选取当前节点。等驾驭self::node() |
以上方式都无法定位到元素的情况下,可以考虑轴定位 | |
![]() |
By.ByXPath("//div[@class=\"qm-header-con\"]//following-sibling::span");
其他方法参考: https://blog.csdn.net/yiqiushi4748/article/details/104037528
三大等待
**- 硬性等待:**强制等待x秒
//缺点:有点浪费时间
Thread.sleep(8);
**- 隐式等待:**在一定时间内只要找到元素就会结束等待,若没找到则会一直等待
//使用场景比较局限(不能判断元素是否可以点击/可见)
chromeDriver.manage().timeouts().implicitlyWait(8000, TimeUnit.MINUTES);
**- 显示等待:**在一定时间内只要找到元素就会结束等待,若未找到元素则会抛出超时等待异常
//实用场景很多如(可见、存在、可点击等),比较推荐
WebDriverWait webDriverWait = new WebDriverWait(chromeDriver,2);
webDriverWait.until(ExpectedConditions.elementToBeClickable(By.xpath("//span[contains(text(),\"立即\")]"))).click();
其他方法参考:https://blog.csdn.net/qq_39704682/article/details/85596644
三大切换
- 切换Iframe
//xxx为frame中的name/id属性
chromeDriver.switchTo().frame("xxx");
//通过xpath进行定位
chromeDriver.switchTo().frame(chromeDriver.findElement(By.xpath("xxx")))
//返回父级Iframe
driver.switchTo().parentFrame();
//返回默认Iframe
driver.switchTo().defaultContent();
- 切换Alert
//获取文本
chromeDriver.switchTo().alert().getText();
//点击确认按钮
……alert.accept();
//取消
……alert.dismiss();
- 切换Window
new WebDriverWait(driver, 8).until(ExpectedConditions.elementToBeClickable(driver.findElement(By.xpath("//a[@aria-label=\"秒杀\"]")))).click();
//遍历所有窗口
for (String i : driver.getWindowHandles()) {
driver.switchTo().window(i);//切换窗口
System.out.println("--------------" + driver.getTitle());
//获取title文本
if ("京东秒杀-正品保证、天天低价、限时限量".equals(driver.getTitle())){System.out.println("进来了:" + driver.getTitle());}
鼠标操作
clickAndHold | 在特定元素上单击鼠标左键(不释放) |
---|---|
release | 在特定元素上释放鼠标左键 |
doubleClick | 在特定元素上双击鼠标左键 |
moveToElement | 移动鼠标指针到特定元素 |
contextClick | 在特定元素上右键单击 |
dragAndDrop | 拖拽元素 |
perform | 调用perform后才会真正执行操作 |
Actions actions= new Actions(driver);
WebElement webElement = driver.findElement(By.xpath("//a[text()=\"我的京东\"]"));
//移动到指定位置
actions.moveToElement(webElement).build().perform();
}
其他操作:https://zhuanlan.zhihu.com/p/396773865
文件上传
- 标签中带有input标签,可以通过senkeys进行上传文件
- 上传文件标签中没有input标签,则需要借助第三方工具如(autoit)官方地址:https://www.autoitscript.com/site/autoit/downloads/
参考:https://www.cnblogs.com/yunman/p/7112882.html#:~:text=selenium%2Bjava%E5%88%A9%E7%94%A8AutoIT%E5%AE%9E%E7%8E%B0%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%201%20AutoIT%E8%84%9A%E6%9C%AC%E7%BC%96%E8%AF%91%E6%88%90%E5%8F%AF%E6%89%A7%E8%A1%8C%E6%96%87%E4%BB%B6%E5%90%8E%EF%BC%8C%E6%94%BE%E5%88%B0%E6%9C%AC%E5%9C%B0%E7%9A%84%E6%9F%90%E4%B8%80%E4%B8%AA%E7%9B%AE%E5%BD%95%E4%B8%8B,2%20%E8%87%AA%E5%8A%A8%E5%8C%96%E5%AE%9E%E7%8E%B0%E8%BF%87%E7%A8%8B%E4%B8%AD%EF%BC%8C%E9%9C%80%E8%A6%81%E4%B8%8A%E4%BC%A0%E5%9B%BE%E7%89%87%E6%97%B6%EF%BC%8C%E9%A6%96%E5%85%88%E5%AE%9A%E4%BD%8D%E5%88%B0%E3%80%90%E4%B8%8A%E4%BC%A0%E3%80%91%E5%AD%97%E6%A0%B7%E6%96%87%E6%9C%AC%EF%BC%8C%E7%82%B9%E5%87%BB%E6%AD%A4%E6%8C%89%E9%92%AE%203%20%E6%89%A7%E8%A1%8C%E7%BC%96%E8%BE%91%E5%90%8E%E7%9A%84%E5%8F%AF%E6%89%A7%E8%A1%8C%E6%96%87%E4%BB%B6%EF%BC%8C%E5%AE%9E%E7%8E%B0%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0
验证码处理
- 测试环境去除验证码
- 找开发添加一个万能验证码绕过校验
- 添加cookie绕过:
//1.代码中添加cookie
Cookie cookie = new Cookie("cookie名称","cookie值");
driver.manage().addCookie(cookie);
//2.手动添加,在浏览器中登录账号,勾选自动登录,下次再次登录就可以不需要验证码
- 图像识别
- 参考:https://zhuanlan.zhihu.com/p/358553292
- JavaScript操作网页元素
document.getElementById(id);
document.getElementsByClassName(className);
//点击
document.getElementById(XX).click();
//修改属性
document.getElementById(XX).setAttribute('属性名','属性值');
//滚动到页面最底部(通用的)
window.scrollTo(0, document.body.scrollHeight);
//滚动到指定元素上去
XXX.scrollIntoViewIfNeeded(true); //XXX为定位到的元素
selenium操作javascript
WebElement kw = chromeDriver.findElement(By.id("kw"));
JavascriptExecutor chromeDriver1 = chromeDriver;
//arguments[0]… + selenium元素定位实现输入值
chromeDriver1.executeScript("arguments[0].value=\"111441\"\n",kw);
//js中实现输入值
chromeDriver1.executeScript("document.getElementById(\"kw\").value=\"111441\"\n");
//
其他方法:https://zhuanlan.zhihu.com/p/403194552
数据驱动
测试中经常会使用相同的测试脚本使用不同的测试数据来执行,测试数据和测试行为完全分离, 这样的测试脚本设计模式称为数据驱动
@Test(dataProvider="userPra")
public void userLogin(String userName, String password, String msg) throws InterruptedException {
System.setProperty("webdriver.chrome.driver", "src/test/resources/chromedriver-win32/chromedriver.exe");
ChromeDriver chromeDriver = new ChromeDriver();
chromeDriver.get("https://www.baidu.com/");
chromeDriver.findElement(By.id("s-top-loginbtn")).click();
WebDriverWait webDriverWait = new WebDriverWait(chromeDriver,8);
webDriverWait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//input[@placeholder=\"手机号/用户名/邮箱\"]"))).sendKeys(userName);
webDriverWait.until(ExpectedConditions.presenceOfElementLocated(By.id("TANGRAM__PSP_11__password"))).sendKeys(password);
webDriverWait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//p[@id=\"TANGRAM__PSP_11__submitWrapper\"]//label"))).click();
webDriverWait.until(ExpectedConditions.presenceOfElementLocated(By.id("TANGRAM__PSP_11__submit"))).click();
String text = chromeDriver.findElement(By.id("TANGRAM__PSP_11__error")).getText();
Assert.assertEquals(msg, text);
}
@DataProvider
public Object[][] userPra() {
Object[][] datas = {
{" ","dededw","请您输入手机号/用户名/邮箱"},
{"rferfer"," ", "请您输入密码"},
{" ","","请您输入手机号/用户名/邮箱"},
{"feewf","fewfewgt","账号或密码错误,请重新输入或者"},
};
return datas;
}
测试日志
//引入log4j依赖
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
//配置文件
#根logger主要定义log4j支持的日志级别及输出目的地
log4j.rootLogger = INFO,console,file
###输出信息到控制台配置###
#表示输出到控制台
log4j.appender.console = org.apache.log4j.ConsoleAppender
#将System.out作为输出
log4j.appender.console.Target = System.out
#使用灵活的布局展示日志信息
log4j.appender.console.layout = org.apache.log4j.PatternLayout
#日志详细输出信息样式
log4j.appender.console.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
###输出信息到文件中配置###
#每天产生一个日志文件
log4j.appender.file = org.apache.log4j.DailyRollingFileAppender
#输出文件目的地
log4j.appender.file.File = log/test.log
#新的日志信息是否追加到旧的日志文件末尾
log4j.appender.file.Append = false
#使用灵活的布局展示日志信息
log4j.appender.file.layout = org.apache.log4j.PatternLayout
#日志详细输出信息样式
log4j.appender.file.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
//需要日志的类下加
private static Logger logger = Logger.getLogger(LogTest.class);
public static void main(String[] args) {
logger.debug("debug级别的日志");
logger.info("info级别的日志");
logger.warn("warn级别的日志");
logger.error("error级别的日志");
}
allure
官方地址:https://allurereport.org/docs/
//引入依赖
<dependency>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-testng</artifactId>
<version>2.12.1</version>
<scope>test</scope>
</dependency>
//设置编码格式
<properties>
<aspectj.version>1.8.10</aspectj.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
</properties>
//引入插件
<build>
<plugins>
<plugin>
<!-- maven-surefire-plugin 配合testng执行测试用例的maven插件 -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
<configuration>
<!-- 测试失败后,是否忽略并继续测试 -->
<testFailureIgnore>true</testFailureIgnore>
<suiteXmlFiles>
<!-- testng配置文件名称 -->
<suiteXmlFile>testng.xml</suiteXmlFile>
</suiteXmlFiles>
<!--设置参数命令行 -->
<argLine>
<!-- UTF-8编码 -->
-Dfile.encoding=UTF-8
<!-- 配置拦截器 -->
-javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
</argLine>
<systemProperties>
<property>
<!-- 配置 allure 结果存储路径 -->
<name>allure.results.directory</name>
<value>${project.build.directory}/allure-results</value>
</property>
</systemProperties>
</configuration>
<dependencies>
<!-- aspectjweaver maven坐标 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
将项目打包
下载allure包并配置环境遍历,然后执行 allure serve 就进入到了allure报告界面
参考:https://blog.csdn.net/qq_36396763/article/details/128816918?ops_request_misc=&request_id=&biz_id=102&utm_term=maven%E5%A6%82%E4%BD%95%E8%BF%9B%E5%85%A5allureweb%E7%95%8C%E9%9D%A2&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-3-128816918.nonecase&spm=1018.2226.3001.4187
失败用例截图截图
//将截图保存到本地
public void screenshot(RemoteWebDriver chromeDriver) {
Date data = new Date();
String format = new SimpleDateFormat("YYYY-MM-dd-HH-mm-ss").format(data);
System.out.println("format:---"+format);
File screenshotAs = ((TakesScreenshot) chromeDriver).getScreenshotAs(OutputType.FILE);
File file = null;
try {
file = new File( new File("").getCanonicalPath()+"\\picture\\"+ format +".png");
System.out.println(file);
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
System.out.println("file:-----"+file);
FileUtils.copyFile(screenshotAs,file);
} catch (Exception e) {
e.printStackTrace();}}
//使用监听器进行截图
public class IHookableDemo implements IHookable {
@Override
public void run(IHookCallBack iHookCallBack, ITestResult iTestResult) {
iHookCallBack.runTestMethod(iTestResult);
if (iTestResult.getThrowable() != null){
System.out.println("222222");
allureText instance = (allureText) iTestResult.getInstance();
new allureText().screenshot(instance.chromeDriver);
}
}
}
//将截图上传至allure
@Attachment(value="screenshot:",type="image/jpg")
public byte[] screen(RemoteWebDriver chromeDriver){
return ((TakesScreenshot)chromeDriver).getScreenshotAs(OutputType.BYTES);