1.简介

其实今天介绍也讲解的也是一种等待的方法,有些童鞋或者小伙伴们会问宏哥,这也是一种等待方法,为什么不在上一篇文章中竹筒倒豆子一股脑的全部说完,反而又在这里单独写了一篇。那是因为这个比较重要,所以宏哥专门为她量身定制了一篇。

FluentWait是Selenium中功能强大的一种等待方式,翻译成中文是流畅等待的意思。在介绍FluentWait之前,我们来讨论下为什么需要设置等待,我们前面介绍了隐式等待和显式等待。在现在很多软件产品为了加强前端的效果,采取了大量的AJAX 和Jquery技术,很多窗体内的数据,需要等待一会,才能加载完数据,才能出现一些元素,driver才能操作这些元素做一些事情。还有就是我们做一些操作,本身可能也需要等待一会才有数据显示。所以在自动化脚本开发过程,合理的设置时间等待是非常必要的,可以说百分之90以上的自动化测试用例执行失败,基本上是很时间等待有关系,造成元素没有及时在界面上显示,而报no such element子类的错误。

2.FluentWait的定义

简单来说,FluentWait就是一个普通的类,我们使用这个类能支持一直等待直到特定的条件出现。

1)是一个类而且是包org.openqa.selenium.support.ui的一部分

2)是Wait接口的一种实现

3)每个Fluent wait,我们可以设置等待最大时间,而且可以做设置等待的频率去检查一些特定的条件。

FluentWait 和 Explicit Wait的区别:简单来说就是Explicit Wait里有一些设定好了的前置条件的等待方式,而Fluent wait你可以设置自己的方法去处理各种等待的问题。

3.核心代码

3.1源码

宏哥先看一下FluentWait的源码,如何查看宏哥这里就不做赘述了。源码如下:

  1. // Licensed to the Software Freedom Conservancy (SFC) under one
  2. // or more contributor license agreements. See the NOTICE file
  3. // distributed with this work for additional information
  4. // regarding copyright ownership. The SFC licenses this file
  5. // to you under the Apache License, Version 2.0 (the
  6. // "License"); you may not use this file except in compliance
  7. // with the License. You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing,
  12. // software distributed under the License is distributed on an
  13. // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  14. // KIND, either express or implied. See the License for the
  15. // specific language governing permissions and limitations
  16. // under the License.
  17.  
  18. package org.openqa.selenium.support.ui;
  19.  
  20. import com.google.common.base.Throwables;
  21. import com.google.common.collect.ImmutableList;
  22.  
  23. import org.openqa.selenium.TimeoutException;
  24. import org.openqa.selenium.WebDriverException;
  25. import org.openqa.selenium.internal.Require;
  26.  
  27. import java.time.Clock;
  28. import java.time.Duration;
  29. import java.time.Instant;
  30. import java.util.ArrayList;
  31. import java.util.Collection;
  32. import java.util.List;
  33. import java.util.function.Function;
  34. import java.util.function.Supplier;
  35.  
  36. /**
  37. * An implementation of the {@link Wait} interface that may have its timeout and polling interval
  38. * configured on the fly.
  39. *
  40. * <p>
  41. * Each FluentWait instance defines the maximum amount of time to wait for a condition, as well as
  42. * the frequency with which to check the condition. Furthermore, the user may configure the wait to
  43. * ignore specific types of exceptions whilst waiting, such as
  44. * {@link org.openqa.selenium.NoSuchElementException NoSuchElementExceptions} when searching for an
  45. * element on the page.
  46. *
  47. * <p>
  48. * Sample usage: <pre>
  49. * // Waiting 30 seconds for an element to be present on the page, checking
  50. * // for its presence once every 5 seconds.
  51. * Wait&lt;WebDriver&gt; wait = new FluentWait&lt;WebDriver&gt;(driver)
  52. * .withTimeout(30, SECONDS)
  53. * .pollingEvery(5, SECONDS)
  54. * .ignoring(NoSuchElementException.class);
  55. *
  56. * WebElement foo = wait.until(new Function&lt;WebDriver, WebElement&gt;() {
  57. * public WebElement apply(WebDriver driver) {
  58. * return driver.findElement(By.id("foo"));
  59. * }
  60. * });
  61. * </pre>
  62. *
  63. * <p>
  64. * <em>This class makes no thread safety guarantees.</em>
  65. *
  66. * @param <T> The input type for each condition used with this instance.
  67. */
  68. public class FluentWait<T> implements Wait<T> {
  69.  
  70. protected static final long DEFAULT_SLEEP_TIMEOUT = 500;
  71.  
  72. private static final Duration DEFAULT_WAIT_DURATION = Duration.ofMillis(DEFAULT_SLEEP_TIMEOUT);
  73.  
  74. private final T input;
  75. private final java.time.Clock clock;
  76. private final Sleeper sleeper;
  77.  
  78. private Duration timeout = DEFAULT_WAIT_DURATION;
  79. private Duration interval = DEFAULT_WAIT_DURATION;
  80. private Supplier<String> messageSupplier = () -> null;
  81.  
  82. private List<Class<? extends Throwable>> ignoredExceptions = new ArrayList<>();
  83.  
  84. /**
  85. * @param input The input value to pass to the evaluated conditions.
  86. */
  87. public FluentWait(T input) {
  88. this(input, Clock.systemDefaultZone(), Sleeper.SYSTEM_SLEEPER);
  89. }
  90.  
  91. /**
  92. * @param input The input value to pass to the evaluated conditions.
  93. * @param clock The clock to use when measuring the timeout.
  94. * @param sleeper Used to put the thread to sleep between evaluation loops.
  95. */
  96. public FluentWait(T input, java.time.Clock clock, Sleeper sleeper) {
  97. this.input = Require.nonNull("Input", input);
  98. this.clock = Require.nonNull("Clock", clock);
  99. this.sleeper = Require.nonNull("Sleeper", sleeper);
  100. }
  101.  
  102. /**
  103. * Sets how long to wait for the evaluated condition to be true. The default timeout is
  104. * {@link #DEFAULT_WAIT_DURATION}.
  105. *
  106. * @param timeout The timeout duration.
  107. * @return A self reference.
  108. */
  109. public FluentWait<T> withTimeout(Duration timeout) {
  110. this.timeout = timeout;
  111. return this;
  112. }
  113.  
  114. /**
  115. * Sets the message to be displayed when time expires.
  116. *
  117. * @param message to be appended to default.
  118. * @return A self reference.
  119. */
  120. public FluentWait<T> withMessage(final String message) {
  121. this.messageSupplier = () -> message;
  122. return this;
  123. }
  124.  
  125. /**
  126. * Sets the message to be evaluated and displayed when time expires.
  127. *
  128. * @param messageSupplier to be evaluated on failure and appended to default.
  129. * @return A self reference.
  130. */
  131. public FluentWait<T> withMessage(Supplier<String> messageSupplier) {
  132. this.messageSupplier = messageSupplier;
  133. return this;
  134. }
  135.  
  136. /**
  137. * Sets how often the condition should be evaluated.
  138. *
  139. * <p>
  140. * In reality, the interval may be greater as the cost of actually evaluating a condition function
  141. * is not factored in. The default polling interval is {@link #DEFAULT_WAIT_DURATION}.
  142. *
  143. * @param interval The timeout duration.
  144. * @return A self reference.
  145. */
  146. public FluentWait<T> pollingEvery(Duration interval) {
  147. this.interval = interval;
  148. return this;
  149. }
  150.  
  151. /**
  152. * Configures this instance to ignore specific types of exceptions while waiting for a condition.
  153. * Any exceptions not whitelisted will be allowed to propagate, terminating the wait.
  154. *
  155. * @param types The types of exceptions to ignore.
  156. * @param <K> an Exception that extends Throwable
  157. * @return A self reference.
  158. */
  159. public <K extends Throwable> FluentWait<T> ignoreAll(Collection<Class<? extends K>> types) {
  160. ignoredExceptions.addAll(types);
  161. return this;
  162. }
  163.  
  164. /**
  165. * @param exceptionType exception to ignore
  166. * @return a self reference
  167. * @see #ignoreAll(Collection)
  168. */
  169. public FluentWait<T> ignoring(Class<? extends Throwable> exceptionType) {
  170. return this.ignoreAll(ImmutableList.<Class<? extends Throwable>>of(exceptionType));
  171. }
  172.  
  173. /**
  174. * @param firstType exception to ignore
  175. * @param secondType another exception to ignore
  176. * @return a self reference
  177. * @see #ignoreAll(Collection)
  178. */
  179. public FluentWait<T> ignoring(Class<? extends Throwable> firstType,
  180. Class<? extends Throwable> secondType) {
  181.  
  182. return this.ignoreAll(ImmutableList.of(firstType, secondType));
  183. }
  184.  
  185. /**
  186. * Repeatedly applies this instance's input value to the given function until one of the following
  187. * occurs:
  188. * <ol>
  189. * <li>the function returns neither null nor false</li>
  190. * <li>the function throws an unignored exception</li>
  191. * <li>the timeout expires</li>
  192. * <li>the current thread is interrupted</li>
  193. * </ol>
  194. *
  195. * @param isTrue the parameter to pass to the {@link ExpectedCondition}
  196. * @param <V> The function's expected return type.
  197. * @return The function's return value if the function returned something different
  198. * from null or false before the timeout expired.
  199. * @throws TimeoutException If the timeout expires.
  200. */
  201. @Override
  202. public <V> V until(Function<? super T, V> isTrue) {
  203. Instant end = clock.instant().plus(timeout);
  204.  
  205. Throwable lastException;
  206. while (true) {
  207. try {
  208. V value = isTrue.apply(input);
  209. if (value != null && (Boolean.class != value.getClass() || Boolean.TRUE.equals(value))) {
  210. return value;
  211. }
  212.  
  213. // Clear the last exception; if another retry or timeout exception would
  214. // be caused by a false or null value, the last exception is not the
  215. // cause of the timeout.
  216. lastException = null;
  217. } catch (Throwable e) {
  218. lastException = propagateIfNotIgnored(e);
  219. }
  220.  
  221. // Check the timeout after evaluating the function to ensure conditions
  222. // with a zero timeout can succeed.
  223. if (end.isBefore(clock.instant())) {
  224. String message = messageSupplier != null ?
  225. messageSupplier.get() : null;
  226.  
  227. String timeoutMessage = String.format(
  228. "Expected condition failed: %s (tried for %d second(s) with %d milliseconds interval)",
  229. message == null ? "waiting for " + isTrue : message,
  230. timeout.getSeconds(), interval.toMillis());
  231. throw timeoutException(timeoutMessage, lastException);
  232. }
  233.  
  234. try {
  235. sleeper.sleep(interval);
  236. } catch (InterruptedException e) {
  237. Thread.currentThread().interrupt();
  238. throw new WebDriverException(e);
  239. }
  240. }
  241. }
  242.  
  243. private Throwable propagateIfNotIgnored(Throwable e) {
  244. for (Class<? extends Throwable> ignoredException : ignoredExceptions) {
  245. if (ignoredException.isInstance(e)) {
  246. return e;
  247. }
  248. }
  249. Throwables.throwIfUnchecked(e);
  250. throw new RuntimeException(e);
  251. }
  252.  
  253. /**
  254. * Throws a timeout exception. This method may be overridden to throw an exception that is
  255. * idiomatic for a particular test infrastructure, such as an AssertionError in JUnit4.
  256. *
  257. * @param message The timeout message.
  258. * @param lastException The last exception to be thrown and subsequently suppressed while waiting
  259. * on a function.
  260. * @return Nothing will ever be returned; this return type is only specified as a convenience.
  261. */
  262. protected RuntimeException timeoutException(String message, Throwable lastException) {
  263. throw new TimeoutException(message, lastException);
  264. }
  265. }

3.2语法

宏哥从源码中的Sample usage提取FluentWait的使用语法如下:

  1. Wait wait = new FluentWait(WebDriver reference)
  2. .withTimeout(timeout, SECONDS)
  3. .pollingEvery(timeout, SECONDS)
  4. .ignoring(Exception.class);
  5.  
  6. WebElement foo=wait.until(new Function<WebDriver, WebElement>() {
  7. public WebElement applyy(WebDriver driver) {
  8. return driver.findElement(By.id("foo"));
  9. }
  10. });

3.3例子

有了语法,按照语法写一个简单例子,如下:

  1. Wait wait = new FluentWait<WebDriver>(driver)
  2. .withTimeout(45, TimeUnit.SECONDS)
  3. .pollingevery(5, TimeUnit.SECONDS)
  4. .ignoring(NoSuchElementException.class);

FluentWait主要使用两个参数–超时值(withTimeout)和轮询频率(pollingevery)。在上面的语法中,我们将超时值设置为45秒,轮询频率设置为5秒。等待条件的最长时间(45秒)和检查指定条件成功或失败的频率(5秒)。如果元素在此时间范围内可以查找到,它将执行下一步操作,否则它将抛出“ElementNotVisibleException”。

3.4完整代码

简单例子的完整代码如下:

  1. package lessons;
  2.  
  3. import java.util.concurrent.TimeUnit;
  4.  
  5. import org.openqa.selenium.By;
  6. import org.openqa.selenium.NoSuchElementException;
  7. import org.openqa.selenium.WebDriver;
  8. import org.openqa.selenium.WebElement;
  9. import org.openqa.selenium.chrome.ChromeDriver;
  10. import org.openqa.selenium.support.ui.FluentWait;
  11. import org.openqa.selenium.support.ui.Wait;
  12.  
  13. import com.google.common.base.Function;
  14.  
  15. public class FluentWait {
  16. public static void main(String[] args) throws Exception {
  17.  
  18. System.setProperty("webdriver.chrome.driver", ".\\Tools\\chromedriver.exe");
  19.  
  20. WebDriver driver = new ChromeDriver();
  21.  
  22. driver.get("www.test.com");
  23. driver.manage().window().maximize();
  24.  
  25. Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
  26.  
  27. .withTimeout(45, TimeUnit.SECONDS)
  28.  
  29. .pollingEvery(5, TimeUnit.SECONDS)
  30.  
  31. .ignoring(NoSuchElementException.class);
  32.  
  33. WebElement ele1 = wait.until(new Function<WebDriver, WebElement>() {
  34.  
  35. public WebElement apply(WebDriver driver) {
  36.  
  37. return driver.findElement(By.id("xxxxxxx"));
  38.  
  39. }
  40.  
  41. });
  42.  
  43. }
  44.  
  45. }

4.项目实战

由于没有现成的网页或者网站以供宏哥进行演示,因此宏哥自己简单写了一个demo以便演示使用。

4.1测试网页代码

宏哥这个网页主要思想就是点击按钮后10s倒计时,倒计时结束出现元素(一段英文文字)。

测试网页test.html,参考代码如下所示:

  1. <html>
  2. <head>
  3. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  4. <title>北京-宏哥</title>
  5. </head>
  6. <style>
  7. #click {
  8. background-color: #4CAF50;
  9. border: none;
  10. color: white;
  11. padding: 15px 32px;
  12. text-align: center;
  13. text-decoration: none;
  14. display: inline-block;
  15. font-size: 16px;
  16. margin: 4px 2px;
  17. cursor: pointer;
  18. }
  19. .button {
  20. background-color: #f44336;
  21. border: none;
  22. color: white;
  23. padding: 15px 32px;
  24. text-align: center;
  25. text-decoration: none;
  26. display: inline-block;
  27. font-size: 28px;
  28. margin-bottom: 100px;
  29. }
  30. #myAnchor
  31. {
  32. text-decoration:none;
  33. color: white;
  34. }
  35. </style>
  36. <body>
  37. <div style=" text-align:center;">
  38. <div style="height: 100px;margin-top: 200px;">
  39. <button class="button"><a id="myAnchor" href="https://www.cnblogs.com/du-hong/">北京-宏哥</a></button></br>
  40. <input type="button" value="Click Me - Fluent Wait" id="click" onclick="foo(this, 10000);"/>
  41. <p style='color:red; font-family: verdana; font-size: 20;align="center";' id="demo">Click and Wait for <b>10 seconds</b> to view a message - "Software Testing Material - DEMO PAGE"</p>
  42. </div>
  43. </div>
  44. </body>
  45. <script>
  46. function foo(obj, time) {
  47. obj.disabled = true;
  48.  
  49. setTimeout(function() {
  50. var x = setInterval(function(){
  51. time= time - 1000; //reduce each second
  52. obj.value = (time/1000)%60;
  53. if(time==0){
  54. clearInterval(x);
  55. obj.value = document.getElementById("demo").innerHTML = "Software Testing Material - DEMO PAGE";
  56. obj.disabled = false;
  57. }
  58. }, 1000);
  59. }, time-10000);
  60. }
  61.  
  62. </script>
  63. </html>

下边宏哥编写java测试脚本。

4.2代码设计

设计思路:打开网页后,点击按钮开始5s频率的轮训查找元素,第一次没有找到,第二次10s刚好出现,代码也轮训查找也刚结束,没有找到,等到第三次英文文字出现了,代码也查找到,结束轮训,继续下一步操作。代码设计如下图所示:

4.3Java参考代码

宏哥首页用单元测试Junit测试一下写的方法有没有问题,没有问题,然后再调用。

4.3.1运行代码

1.运行代码,右键Run AS->JUnit Test,控制台输出,绿色的进度条证明写的方法没有问题,而且控制台也循环了2次(每次5s,一共10s),等待到了元素的出现并将其打印出来。如下图所示:

2.运行代码后电脑端的浏览器的动作,如下小视频所示:

4.4Java优化参考代码

通过上边的单元测试我们知道写的方法没有问题,那么下边我们直接调用该方法即可。优化后代码如下:

  1. package lessons;
  2. import org.junit.Test;
  3.  
  4. import java.util.NoSuchElementException;
  5. import java.util.concurrent.TimeUnit;
  6.  
  7. import org.openqa.selenium.By;
  8. import org.openqa.selenium.WebDriver;
  9. import org.openqa.selenium.WebElement;
  10. import org.openqa.selenium.chrome.ChromeDriver;
  11. import org.openqa.selenium.support.ui.FluentWait;
  12.  
  13. import com.google.common.base.Function;
  14.  
  15. /**
  16. * @author 北京-宏哥
  17. *
  18. *《手把手教你》系列技巧篇(二十五)-java+ selenium自动化测试-FluentWait(详细教程)
  19. *
  20. * 2021年8月31日
  21. */
  22. public class FluentWaitClass {
  23.  
  24. @Test
  25. public static void fluentWaitMethod(){
  26. System.setProperty("webdriver.gecko.driver", ".\\Tools\\chromedriver.exe"); //指定驱动路径
  27. WebDriver driver = new ChromeDriver();
  28. //最大化窗口
  29. long startTime = System.currentTimeMillis(); //获取开始时间
  30. driver.manage().window().maximize();
  31. long endTime = System.currentTimeMillis(); //获取结束时间
  32. System.out.println("程序运行时间1:" + (endTime - startTime) + "ms"); //输出程序运行时间
  33. driver.get("file:///C:/Users/DELL/Desktop/test/test.html");
  34. driver.findElement(By.xpath("//*[@id='click']")).click();
  35.  
  36. FluentWait<WebDriver> wait = new FluentWait<WebDriver>(driver)
  37. .withTimeout(45, TimeUnit.SECONDS)
  38. .pollingEvery(5, TimeUnit.SECONDS)
  39. .ignoring(NoSuchElementException.class);
  40. long startTime1 = System.currentTimeMillis(); //获取开始时间
  41. WebElement element = wait.until(new Function<WebDriver, WebElement>() {
  42. public WebElement apply(WebDriver driver) {
  43. WebElement element = driver.findElement(By.xpath("//*[@id='demo']"));
  44. String getTextOnPage = element.getText();
  45. if(getTextOnPage.equals("Software Testing Material - DEMO PAGE")){
  46. System.out.println(getTextOnPage);
  47. return element;
  48. }else{
  49. System.out.println("FluentWait Failed");
  50. return null;
  51. }
  52. }
  53. });
  54. long endTime1 = System.currentTimeMillis(); //获取结束时间
  55. System.out.println("程序运行时间3:" + (endTime1 - startTime1) + "ms"); //输出程序运行时间
  56. //driver.close();
  57. }
  58.  
  59. public static void main(String [] args){
  60. long startTime = System.currentTimeMillis(); //获取开始时间
  61. fluentWaitMethod();
  62. long endTime = System.currentTimeMillis(); //获取结束时间
  63. System.out.println("程序运行时间2:" + (endTime - startTime) + "ms"); //输出程序运行时间
  64. }
  65. }
4.4.1运行代码

1.运行代码,右键Run AS->java Application,控制台输出,如下图所示:

2.运行代码后电脑端的浏览器的动作,如下小视频所示:

5.小结

1.在设计代码过程中会报错:Type mismatch: cannot convert from Test to Annotation   如下图所示:

查了好多都说是:类名不能和注解名称相同的原因。后来宏哥检查了一下,不相同啊,但是宏哥为啥这里还会报这个错了。原来是宏哥没有导入单元测试的包,但是也没有提示导入包,因此宏哥将包导入,代码错误消失。如下图所示:

  好了,今天就分享到这里了,感谢你耐心的阅读!

《手把手教你》系列技巧篇(二十五)-java+ selenium自动化测试-FluentWait(详细教程)的更多相关文章

  1. 《手把手教你》系列技巧篇(十)-java+ selenium自动化测试-元素定位大法之By class name(详细教程)

    1.简介 按宏哥计划,本文继续介绍WebDriver关于元素定位大法,这篇介绍By ClassName.看到ID,NAME这些方法的讲解,小伙伴们和童鞋们应该知道,要做好Web自动化测试,最好是需要了 ...

  2. 《手把手教你》系列技巧篇(十七)-java+ selenium自动化测试-元素定位大法之By css上卷(详细教程)

    1.简介 CSS定位方式和xpath定位方式基本相同,只是CSS定位表达式有其自己的格式.CSS定位方式拥有比xpath定位速度快,且比CSS稳定的特性.下面详细介绍CSS定位方式的使用方法.xpat ...

  3. 《手把手教你》系列技巧篇(六)-java+ selenium自动化测试-阅读selenium源码(详细教程)

    1.简介 前面几篇基础系列文章,足够你迈进了Selenium门槛,再不济你也至少知道如何写你第一个基于Java的Selenium自动化测试脚本.接下来宏哥介绍Selenium技巧篇,主要是介绍一些常用 ...

  4. 《手把手教你》系列技巧篇(十一)-java+ selenium自动化测试-元素定位大法之By tag name(详细教程)

    1.简介 按宏哥计划,本文继续介绍WebDriver关于元素定位大法,这篇介绍By ClassName.看到ID,NAME这些方法的讲解,小伙伴们和童鞋们应该知道,要做好Web自动化测试,最好是需要了 ...

  5. 《手把手教你》系列技巧篇(十三)-java+ selenium自动化测试-元素定位大法之By partial link text(详细教程)

    1.简介 本文按计划就要开始介绍partial link text,顾名思义是通过链接定位的(官方说法:超链接文本定位).什么是partial link text呢,看到part这个单词我们就可以知道 ...

  6. 《手把手教你》系列技巧篇(七)-java+ selenium自动化测试-宏哥带你全方位吊打Chrome启动过程(详细教程)

    1.简介 经过前边几篇文章和宏哥一起的学习,想必你已经知道了如何去查看Selenium相关接口或者方法.一般来说我们绝大多数看到的是已经封装好的接口,在查看接口源码的时候,你可以看到这个接口上边的注释 ...

  7. 《手把手教你》系列技巧篇(八)-java+ selenium自动化测试-元素定位大法之By id(详细教程)

    1.简介 从这篇文章开始,要介绍web自动化核心的内容,也是最困难的部分了,就是:定位元素,并去对定位到的元素进行一系列相关的操作.想要对元素进行操作,第一步,也是最重要的一步,就是要找到这个元素,如 ...

  8. 《手把手教你》系列技巧篇(九)-java+ selenium自动化测试-元素定位大法之By name(详细教程)

    1.简介 上一篇宏哥已经介绍了通过id来定位元素,今天继续介绍其他剩下的七种定位方法中的通过name来定位元素.本文来介绍Webdriver中元素定位方法之By name,顾名思义,就是我们想要定位的 ...

  9. 《手把手教你》系列技巧篇(十五)-java+ selenium自动化测试-元素定位大法之By xpath中卷(详细教程)

    1.简介 按宏哥计划,本文继续介绍WebDriver关于元素定位大法,这篇介绍定位倒数二个方法:By xpath.xpath 的定位方法, 非常强大.  使用这种方法几乎可以定位到页面上的任意元素. ...

随机推荐

  1. ES读写数据的工作原理

    es写入数据的工作原理是什么啊?es查询数据的工作原理是什么?底层的lucence介绍一下呗?倒排索引了解吗? 一.es写数据过程 1.客户端选择一个node发送请求过去,这个node就是coordi ...

  2. HashSet的add()方法源码解析(jdk1.8)

    HashSet 实现了Set接口 实际上是HashMap 可以存null,但只能有一个 不保证元素是有序的,取决于hash后,在确定索引结果 add源码 //核心操作putVal final V pu ...

  3. Leaflet 操作OSM(OpenStreetMap) 设置地图style

    Leaflet是一个开源的地图操作库,其中mapbox是其一个插件,这个插件可以自定义想要的地图格式.https://www.mapbox.com/mapbox-gl-js/api/这是其官网地址. ...

  4. CSS 是啥?前端小白入门级理解

    What is CSS? CSS stands for Cascading Style Sheets CSS describes how HTML elements are to be display ...

  5. vue + WangEnduit

    components 注册组件 <template lang="html"> <div class="editor"> <div ...

  6. Appium问题解决方案(6)- Java堆栈错误:java.lag.ClassNotFoundException:org.eclipse.swt.widets.Control

    背景 运行脚本出现 SWT folder '..\lib\location of your Java installation.' does not exist. Please set ANDROID ...

  7. 【曹工杂谈】Maven IOC容器的下半场:Google Guice

    Maven容器的下半场:Guice 前言 在前面的文章里,Maven底层容器Plexus Container的前世今生,一代芳华终落幕,我们提到,在Plexus Container退任后,取而代之的底 ...

  8. ClickOnce 获取客户端发布版本号

    https://social.microsoft.com/Forums/es-ES/26786b8d-0155-4261-9672-11b786d8c1d6/clickonceandsetup /// ...

  9. vue-cookies使用

    一.安装 vue-cookies npm install vue-cookies --save 二.引入并声明使用 import Vue form 'Vue' import VueCookies fr ...

  10. HDU1213How Many Tables(基础并查集)

    HDU1213How Many Tables Problem Description Today is Ignatius' birthday. He invites a lot of friends. ...