我们更多的是在 Jenkins Pipeline 中使用 Selenium 框架,因此需要使用 Groovy 类库。由于没有与之对应的 Groovy 类库,因此只能使用 Java 类库。
还有另外种做法:使用 Python 实现,然后在 Groovy 中命令行调用。但是我们无法使用该方法,因为 Selenime 的自动化测试过程中需要交互、判断,而这种方法无法获取状态,只能输入执行然后等待输出。
Maven Repository: org.seleniumhq.selenium » selenium-java(我们使用 Selenium Grid 3 版本) 下载页面:Downloads 接口文档:https://www.selenium.dev/selenium/docs/api/java/index.html
Selenium with Java: Best Practices
python - How do I set browser width and height in Selenium WebDriver? - Stack OverflowSelenium Waits: Implicit, Explicit, Fluent And Sleep
import org.openqa.selenium.Dimension; import org.openqa.selenium.Point; webDriver.manage().window().setPosition(new Point(0, 0)); webDriver.manage().window().setSize(new Dimension(1366, 768)); // 窗口最大化 webDriver.manage().window().maximize();Find Element and FindElements in Selenium WebDriver
// 最常规的用法:通过 ID 选择元素 webDriver.findElement(By.id("buttoncheck")) // 通过 XPath 选择元素 webDriver.findElement(By.xpath("//div[@id='writeArticleWrapper']//form//input[@type='text' and @name='title']"));定位元素的方法有很多,比如 ID、Name、Class Name、Tag Name、Link Text、Partial Link Text、XPATH 等等
我们没有找到保存 Cookie 的专有方法,所以我们采用自己的方案:1)将 Cookie 对象保存到文件,2)启动时再载入 Cookie 对象
将 Cookie 保存到文件:
private void cookieWriteToFile(WebDriver webDriver) throws Exception { ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("/path/to/cookie.bin")); objectOutputStream.writeObject(webDriver.manage().getCookies()); }从文件中读取 Cookie:
private void cookieWriteToFile(WebDriver webDriver) throws Exception { ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("/path/to/cookie.bin")); Set<Cookie> cookies = (Set<Cookie>) objectInputStream.readObject(); for (Cookie cookie : cookies) { webDriver.manage().addCookie(cookie); } }该方法的本质是:保存二进制对象到文件,之后从文件恢复二进制对象
注意事项,该方法适用于 Java 语言,而 Groovy 语言存在其他问题,参考 Write an Object to File and Read it 笔记。
Using Thread.sleep() in Selenium WebDriver - Make Selenium Easy
当我们加载页面后,可能需要等待页面渲染,等待某个 HTML 元素加载完成。我们经常使用 Thread.sleep() 进行等待,但是具有以下缺点: 1)等待时间过长,而页面已经加载完成;等待时间过短,而页面还未加载完成; 2)我们无法确定要等待的具体时间。如果使用 while 循环检查,程序会显得“不整洁”; 3)每个查找元素的地方都需要等待; 4)必须等待特定时间后,即 Thread.sleep() 设置的时间,才能继续执行后续程序;
我们可以使用 Selenium 提供的等待方法: 1)Implicit wait – Provided by Selenium WebDriver 2)Explicit wait (WebDriverWait & FluentWait) Provided by Selenium WebDriver 3)Fluent Wait
如下是演示代码(只包含关键部分),我们通过演示代码进行讲解:
WebDriver driver=new ChromeDriver(); driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS); driver.get("https://www.easemytrip.com/"); driver.findElement(By.id("FromSector_show")).sendKeys("Delhi", Keys.ENTER); driver.findElement(By.id("Editbox13_show")).sendKeys("Mumbai", Keys.ENTER);如上示例,使用 implicitlyWait 最多 30s 等待,具有以下优势: 1)在 findElement 时,最多 30s 等待,只要找元素就立即向下执行; 2)如果在 30s 内没有找到,则返回 ElementNotVisibleException 异常; 3)全局设置(只需要设置一次,无需在每次查找元素时进行设置);
但是我们会遇到另外场景,比如:虽然 HTML 元素已经找到,但是在页面元素是否可见、是否可以点击,这些会影响自动化测试的进行。针对这个问题,我们可以使用 Explicit wait 等待。
如下是演示代码(只包含关键部分),我们通过演示代码进行讲解:
WebDriver driver = new ChromeDriver(); driver.get("https://www.rentomojo.com/"); // 等待页面元素可见 WebDriverWait wait = new WebDriverWait(driver, 120); wait.until(ExpectedConditions.visibilityOf(driver.findElement(By.xpath("//div[@class='Campaign__innerWrapper']/button")))); driver.findElement(By.xpath("//div[@class='Campaign__innerWrapper']/button")).click(); // 等待 body 中出现内容 // https://stackoverflow.com/questions/15656252/wait-till-text-present-in-text-field/15657053 new WebDriverWait(driver, 120).until(new ExpectedCondition<Boolean>() { @Override public Boolean apply(WebDriver input) { WebElement bodyElement = input.findElement(By.xpath("html/body")); return !"".equals(bodyElement.getAttribute("innerHTML").trim()); } });如上程序,通过 visibilityOf 方法等待,直到特定元素可见。通过该方法可以判断某些 HTML 元素是否已经处于特定状态。还有很多其他状态,参考 ExpectedConditions 文档。
类似与 Explicit wait 等待,但是更加灵活,可以自定义等待时间粒度、忽略异常等等:
Wait<WebDriver> fluentWait = new FluentWait<WebDriver>(driver) .withTimeout(60, TimeUnit.SECONDS) // // this defines the polling frequency .pollingEvery(2, TimeUnit.SECONDS) .ignoring(NoSuchElementException.class); // this defines the exception to ignore WebElement foo = fluentWait.until(new Function<WebDriver, WebElement>() { // in this method defined your own subjected conditions for which // we need to wait for public WebElement apply(WebDriver driver) { return driver.findElement(By.id("foo")); } });注意事项,我们没有使用过 Fluent Wait 等待,这里只是简单整理,详细方法需要参考官方文档。
How to get HTML source of a Web Element in Selenium WebDriver | BrowserStack
element.getAttribute("innerHTML");Modify innerHTML using Selenium - Stack Overflow
WebElement element = ... ((JavascriptExecutor)driver).executeScript("arguments[0].innerHTML = '<h1>H1</h1>';", element);java - put a string with html/Javascript into selenium webdriver - Stack Overflow
在我们的 HTML 内容中,经常会包含复杂的内容,比如单引号、双引号,会破坏 Javascript 语法,导致代码无法执行。
解决方法如下(如下是 Groovy 代码):
@Grab(group='commons-lang', module='commons-lang', version='2.6') // 正好的 Jenkins 所依赖的版本一致 import org.apache.commons.lang.StringEscapeUtils; String htmlContent = StringEscapeUtils.escapeJavaScript(postInfo.content) ((JavascriptExecutor) webDriver).executeScript("arguments[0].innerHTML = '${htmlContent}';", bodyElement);java - Wait for page load in Selenium - Stack Overflowjava - Selenium -- How to wait until page is completely loaded - Stack Overflow
new WebDriverWait(webDriver, 30).until(new Function<WebDriver, Boolean>() { @Override public Boolean apply(WebDriver tmpWebDriver) { Object pageReady = ((JavascriptExecutor) tmpWebDriver).executeScript("return document.readyState;"); return "complete".equals(pageReady.toString()); } });K4NZ / 使用 Java 语言Selenium Grid Tutorial: Hub & Node (with Example)Selenium Java / API / Overview
