selenium初探

這一週導師讓我研究selenium。selenium支持很多語言,而我使用的是js來測試。需要安裝selenium-webdriver: ^3.5.

基礎篇

我參考了很多文章,這裏列下其中一下:
1、
http://jeremy-xu.oschina.io/2016/05/22/web%E7%95%8C%E9%9D%A2%E6%B5%8B%E8%AF%95%E5%AE%9E%E8%B7%B5%E4%B9%8BSelenium-WebDriver/
這篇文章詳細列出了webdriver的api,包括定位UI元素對UI元素的操作在窗口或Frame間移動操作Alert窗口操作瀏覽器的導航及地址欄操作Cookie操作窗口高級用戶接口操作等待until的用法…只能說是乾貨多多。給我帶來了不少便利。

2、
https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebDriver.html
這裏是官網api的介紹。詳細介紹了selenium-webdriver的一些操作。但是沒有until相關操作。很多不確定的東西來這裏看一看都可以找到答案。

3、
http://www.cnblogs.com/fnng/p/5854875.html
蟲師教你如何入門。蟲師對測試方面有很深入的瞭解並寫了Selenium 2自動化測試實戰 基於pyton語言一書。不過似乎也有java版本的。

這些入門是非常非常快的。上面寫的太好了,我也就不重複了。

進階篇

這裏主要講講我我遇到的錯誤。

Element is not clickable at point (411, 675). Other element would receive the click

元素不可點擊錯誤。這種情況可能有原因有:

元素不可見。比如不在可見的視圖之內。關於元素可見性,請見https://www.w3.org/TR/webdriver/#element-displayedness

這個的解決有兩種。
①通過action

driver.actions().mouseMove(ele).click().perform(); //記得加上perform()

②通過js

driver.executeScript("scroll(x,y)"); //x,y是座標

或者

driver.executeScript("arguments[0].scrollIntoView()", webElement);

第一個參數也可以是函數:

driver.executeScript(() => {
  domElement.scrollIntoView();    
})

頁面正在刷新,所以無法點擊元素。這種情況下可以等頁面加載完成。

driver.wait(function() {
  driver.executeScript(() => {
    return document.readyState;
  })
  .then(state => {
    driver.isOk = (state === "complete");
  })
  return driver.isOk;
})

或者使用driver.sleep(ms)。不過這種機械式的等待我並不喜歡。

該元素被其他元素擋住了。這種情況下需要等到你的目標元素可見。

driver.wait(until.elementIsVisible(webElement))

在這種情況下通過腳本執行點擊也是可以的。

NoSuchElementError: no such element: Unable to locate element:

可能的原因有:

選擇器使用錯誤。使用By.xpath容易犯錯。

不在一個frame,這是經常遇到的錯誤。

比如你的某個頁面加載了一個iframe。而你要定位iframe中的元素。這時候就會報這個錯誤。所以需要定位到這個iframe。

driver.switchTo().frame(id);
driver.findElement(...);

然後又要重定位主頁面的元素,需要再次切換回到主頁面。

driver.switchTo().defaultContent();

不在一個window中,這時候需要切換window。切換window不像切換iframe那麼簡單。需要拿到window的句柄。

driver.getAllWindowHandles(); // 獲取所有window的句柄,返回一個數組
driver.getWindowHandle(); // 獲取當前window的句柄

然後遍歷所有的window的數組,通過driver.getText()driver.getCurrentUrl()等來判斷是不是你所需要的window。然後:

driver.switchTo().window(string)

這一種原因更常見,比如一個異步請求,請求完成時某個元素纔會在頁面顯示,但是代碼執行的速度非常非常快,執行到這裏的時候元素還沒出現。

可以將driver.waitdriver.findElement結合來解決。代碼如下:

driver.wait(() => {
  $_(By.css('.group-member-list .search-result-student-item')).then(() => {
    driver.isOk = true;
  }, () => {
    driver.isOk = false;
  })
  return driver.isOk;
})
delete driver.isOk;

StaleElementReferenceError: stale element reference: element is not attached to the page document

這個問題是一個非常煩的問題。我曾爲它花了兩天時間。爲此我也找過很多資料。http://docs.seleniumhq.org/exceptions/stale_element_reference.jsp這裏是官方給出的原因。

元素被刪除了

1.頁面部分刷新(ajax)或者導航到另一個頁。
2.頁面上的元素被js框架替換成了一個一模一樣的元素。這樣的話元素就是stale的。雖然頁面上觀察不到任何變化。實際上,我一直懷疑我的是這種原因,頁面使用了react,我懷疑當我作出某處操作的時候,頁面重新渲染了。後來的元素和之前的元素一模一樣,但是我已經丟失了之前元素的綁定。

元素不再綁定到dom

比如有一個tab,我們在tab上做切換操作的時候,實際上我們切換的是dom。這時候,之前dom上的元素我們不再使用(頁面上不再使用),變成stale的了。

除了這些之外,我搜索其他資料的時候也發現一些原因:

元素被重定位到頁面其他位置

元素通過ajax重新渲染到頁面

做過渡效果的時候,比如有一個div,想要讓一個新的div來代替它。這時候clone老的div,讓它移出去,而新的div移進來(這塊我也不是很理解,文末有相關鏈接)

爲了解決(儘可能解決)這種錯誤,可能的解決方法有:

每次重新定位元素而不是存儲對元素的引用

每次使用 driver.findElement() 來查找元素而不是:

let ele = driver.findElement()
......
ele.xxx
因爲ele可能會丟失綁定

利用js庫的鉤子

比如jquery中,動畫有一個隊列。可以查詢一個元素上是否有動畫。如果元素處於動畫中,可能會報這個錯誤。

僞代碼
driver.wait(() => {
  return animationQueue === 0
})

等一個元素變成stale

如果知道一個元素會變成stale,那麼就等它變成stale再去操作它。until中有一個方法。

driver.wati(until.stalenessOf(ele));

源碼如下:

exports.stalenessOf = function stalenessOf(element) {
  return new Condition('element to become stale', function() {
    return element.getTagName().then(
        function() { return false; },
        function(e) {
          if (e instanceof error.StaleElementReferenceError) {
            return true;
          }
          throw e;
        });
  });
};

js中設置window下的變量

$.ajax(url)
.then()
.always(() => {
  window.__ISCOMPLETED__ = true;
}) 

然後在selenium中:

driver.wait(() => {
  dirver.executeScript(() => {
    driver.isOk = (window.__ISCOMPLETED__ = true);
  })
  return driver.isOk;
})

將操作移到js而不是使用selenium的操作

driver.executeScript(() => {
  documnet.querySelector(selector).click();
})

我就是使用這種方法的。

另外需要說明一下,永遠不要使用try/catch。我不敢說selenium中所有操作是異步的。但是try/catch無法捕捉錯誤。你需要使用:

driver.xxxxxx
.then()
.catch()

番外篇

來自:
http://huziketang.com/blog/posts/detail?postId=58d50da37413fc2e8240855c

想說點關於使用 await 的一些話

你在可能網絡上其他地方看到一些例子,它們並沒有使用 async/await,或者是使用了promise。實際上這樣的代碼是同步的。那麼爲什麼也能 work 的很好呢?坦白地說,我也不知道,看起來像是在 webdriver 中有些trick 的處理。正如 selenium 文檔中說道,在 Node 支持 async/await 之前,這是一個臨時的解決方案。

Selenium 文檔是 Java 語言。它還不完整,但是包含的信息也足夠了,你做幾次測試就能掌握這個技能。

來自:
http://engineeringquality.blogspot.hk/2013/08/ways-of-dealing-with.html

Is that in reality, the selection and the click action will actually be sent as 2 separate calls when it goes over the wire on Selenium’s JSON wire protocol. Doing this all in JavaScript will ensure the call gets executed in the same frame.

就是說我們使用:

driver.findElement().click()

的時候,selenium會分成兩步處理。這是由於JSON wire protocol規定的。

參考

https://stackoverflow.com/questions/11908249/debugging-element-is-not-clickable-at-point-error

http://engineeringquality.blogspot.hk/2013/08/ways-of-dealing-with.html

http://huziketang.com/blog/posts/detail?postId=58d50da37413fc2e8240855c

http://jeremy-xu.oschina.io/2016/05/22/web%E7%95%8C%E9%9D%A2%E6%B5%8B%E8%AF%95%E5%AE%9E%E8%B7%B5%E4%B9%8BSelenium-WebDriver/

有問題歡迎聯繫我,可以一起探討。
郵箱:[email protected]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章