自動化測試selenium在小公司的成功實踐

邵磊發表於2018-06-17
本文可能是目前最完整的一篇selenium(java版)實踐文章,不是之一。
如果你是java開發人員,本文將幫助你快速搭建整套selenium自動化測試框架,你可以幫助公司升級為自動化測試架構;
如果你是測試人員,那你得按照本文多實踐一下,遇到不懂的諮詢下公司的java開發,同樣你也可以完成自動化測試架構升級。
當然啦,如果目前公司已經是自動化測試了,那本文就當是再次梳理下相關知識吧。
複製程式碼

前言

可能提到自動化測試selenium,大家都會想到用python語言來編寫指令碼。但我們選擇了java語言,因為我相信大部分公司java程式設計師比python程式設計師多得多。而對於很多測試人員,並不能熟練使用程式語言,所以他們需要別人指導。與其使用更簡單的python語言,卻看不懂語法,得不到別人幫助;那還不如使用java語言,無論是語法還是程式設計思路,都可以快速獲得java開發人員的幫助。

背景

可能很多公司已經有標準的後端單元測試程式碼,但是自動化測試需要測試整個系統,前端是直接展示給使用者的,所以,前端尤為重要,本文就是基於h5的web前端自動化測試。當然啦,這裡推薦對專案進行前後端分離,如果專案沒有前後端分離可參考某小公司RESTful、共用介面、前後端分離、介面約定的實踐

目前網際網路上關於selenium完整的文章很少,也很難買到一個專門講selenium的書籍,這讓很多測試人員無從下手,而本文會彌補這一問題,儘可能詳細完整介紹selenium的實踐,提供一個簡易版的完整專案程式碼在github上(因為公司專案程式碼沒有脫敏,不能直接放到github上)。

相關知識

  1. html標籤
  2. css樣式
  3. js基礎
  4. java基礎
  5. bat指令碼基礎

首先html由標籤<x></x>組成,詳細本文會在真實專案中一一介紹。

正式實踐

安裝火狐瀏覽器

因為selenium在火狐瀏覽器裡,可以自動化錄製指令碼,我們通過指令碼錄製可以生成出不同的語言指令碼,可以省去我們90%的編寫指令碼工作量。
可以安裝最新版的火狐瀏覽器,然後安裝Katalon Recorder (Selenium IDE for Firefox)
使用火狐瀏覽器開啟https://addons.mozilla.org/zh-CN/firefox/addon/katalon-automation-record/?src=search

自動化測試selenium在小公司的成功實踐

錄製指令碼

以百度搜尋掘金為例

  1. 位址列開啟百度
  2. 右上角,開啟Katalon擴充套件
  3. 點選Katalon的New
  4. 點選 Record
  5. 網頁中輸入 掘金網
  6. 開啟第一個掘金官網
  7. 在掘金官網搜尋我以前寫的一篇文章 我是如何重構整個研發專案,促進自動化運維DevOps的落地?
  8. 點選第一條 我是如何重構整個研發專案,促進自動化運維DevOps的落地?
  9. 點選Katalon的stop
    自動化測試selenium在小公司的成功實踐

每執行一個操作右下角都會提示

自動化測試selenium在小公司的成功實踐

錄製後的效果圖

自動化測試selenium在小公司的成功實踐

執行、分析指令碼

錄製後,我們點選一下play,可以看到火狐瀏覽器自動化的完成了我們剛剛的操作(關閉彈窗阻止,或者將掘金和百度加入不阻止彈窗列表)

自動化測試selenium在小公司的成功實踐

點選Export

自動化測試selenium在小公司的成功實踐

可以看到有各種語言 C#、Java、katalon、python2等。
我們先看看python2的指令碼

# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoAlertPresentException
import unittest, time, re

class Test(unittest.TestCase):
    def setUp(self):
        self.driver = webdriver.Firefox()
        self.driver.implicitly_wait(30)
        self.base_url = "https://www.katalon.com/"
        self.verificationErrors = []
        self.accept_next_alert = True
    
    def test_(self):
        driver = self.driver
        driver.get("https://www.baidu.com/index.php?tn=monline_3_dg")
        driver.find_element_by_id("kw").click()
        driver.find_element_by_id("kw").clear()
        driver.find_element_by_id("kw").send_keys(u"掘金網")
        driver.find_element_by_xpath("//div[@id=`container`]/div[2]/div").click()
        driver.find_element_by_link_text(u"掘金- juejin.im - 一個幫助開發者成長的社群").click()
        # ERROR: Caught exception [ERROR: Unsupported command [selectWindow | win_ser_1 | ]]
        driver.find_element_by_xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input").click()
        driver.find_element_by_xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input").click()
        driver.find_element_by_xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input").clear()
        driver.find_element_by_xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input").send_keys(u"我是如何重構整個研發專案,促進自動化運維DevOps的落地?")
        driver.find_element_by_xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input").send_keys(Keys.ENTER)
        driver.find_element_by_link_text(u"我是如何重構整個研發專案,促進自動化運維DevOps的落地?").click()
    
    def is_element_present(self, how, what):
        try: self.driver.find_element(by=how, value=what)
        except NoSuchElementException as e: return False
        return True
    
    def is_alert_present(self):
        try: self.driver.switch_to_alert()
        except NoAlertPresentException as e: return False
        return True
    
    def close_alert_and_get_its_text(self):
        try:
            alert = self.driver.switch_to_alert()
            alert_text = alert.text
            if self.accept_next_alert:
                alert.accept()
            else:
                alert.dismiss()
            return alert_text
        finally: self.accept_next_alert = True
    
    def tearDown(self):
        self.driver.quit()
        self.assertEqual([], self.verificationErrors)

if __name__ == "__main__":
    unittest.main()

複製程式碼

我們再看看java junit指令碼

package com.example.tests;

import java.util.regex.Pattern;
import java.util.concurrent.TimeUnit;
import org.junit.*;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
import org.openqa.selenium.*;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.ui.Select;

public class Test {
  private WebDriver driver;
  private String baseUrl;
  private boolean acceptNextAlert = true;
  private StringBuffer verificationErrors = new StringBuffer();

  @Before
  public void setUp() throws Exception {
    driver = new FirefoxDriver();
    baseUrl = "https://www.katalon.com/";
    driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
  }

  @Test
  public void test() throws Exception {
    driver.get("https://www.baidu.com/index.php?tn=monline_3_dg");
    driver.findElement(By.id("kw")).click();
    driver.findElement(By.id("kw")).clear();
    driver.findElement(By.id("kw")).sendKeys("掘金網");
    driver.findElement(By.xpath("//div[@id=`container`]/div[2]/div")).click();
    driver.findElement(By.linkText("掘金- juejin.im - 一個幫助開發者成長的社群")).click();
    // ERROR: Caught exception [ERROR: Unsupported command [selectWindow | win_ser_1 | ]]
    driver.findElement(By.xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input")).click();
    driver.findElement(By.xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input")).click();
    driver.findElement(By.xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input")).clear();
    driver.findElement(By.xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input")).sendKeys("我是如何重構整個研發專案,促進自動化運維DevOps的落地?");
    driver.findElement(By.xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input")).sendKeys(Keys.ENTER);
    driver.findElement(By.linkText("我是如何重構整個研發專案,促進自動化運維DevOps的落地?")).click();
  }

  @After
  public void tearDown() throws Exception {
    driver.quit();
    String verificationErrorString = verificationErrors.toString();
    if (!"".equals(verificationErrorString)) {
      fail(verificationErrorString);
    }
  }

  private boolean isElementPresent(By by) {
    try {
      driver.findElement(by);
      return true;
    } catch (NoSuchElementException e) {
      return false;
    }
  }

  private boolean isAlertPresent() {
    try {
      driver.switchTo().alert();
      return true;
    } catch (NoAlertPresentException e) {
      return false;
    }
  }

  private String closeAlertAndGetItsText() {
    try {
      Alert alert = driver.switchTo().alert();
      String alertText = alert.getText();
      if (acceptNextAlert) {
        alert.accept();
      } else {
        alert.dismiss();
      }
      return alertText;
    } finally {
      acceptNextAlert = true;
    }
  }
}

複製程式碼

python程式碼量明細比java要少一點,但是本文講java語言實踐。

我們主要關注 java版 @Test註解的那個test方法

    driver.get("https://www.baidu.com/index.php?tn=monline_3_dg");
    driver.findElement(By.id("kw")).click();
    driver.findElement(By.id("kw")).clear();
    driver.findElement(By.id("kw")).sendKeys("掘金網");
    driver.findElement(By.xpath("//div[@id=`container`]/div[2]/div")).click();
    driver.findElement(By.linkText("掘金- juejin.im - 一個幫助開發者成長的社群")).click();
    // ERROR: Caught exception [ERROR: Unsupported command [selectWindow | win_ser_1 | ]]
    driver.findElement(By.xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input")).click();
    driver.findElement(By.xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input")).click();
    driver.findElement(By.xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input")).clear();
    driver.findElement(By.xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input")).sendKeys("我是如何重構整個研發專案,促進自動化運維DevOps的落地?");
    driver.findElement(By.xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input")).sendKeys(Keys.ENTER);
    driver.findElement(By.linkText("我是如何重構整個研發專案,促進自動化運維DevOps的落地?")).click();
複製程式碼

可能很多人已經能看懂了

driver.get(“https://www.baidu.com/index.php?tn=monline_3_dg”);

開啟百度

driver.findElement(By.id(“kw”)).click();

通過id定位到html標籤,然後點選click();清空文字框.clear();輸入 掘金網3個字 sendKeys(“掘金網”);

這裡我們看一下百度的搜尋框程式碼

自動化測試selenium在小公司的成功實踐
<input type="text" class="s_ipt" name="wd" id="kw" maxlength="100" autocomplete="off">
複製程式碼

driver.findElement(By.linkText(“掘金- juejin.im – 一個幫助開發者成長的社群”)).click();

單擊掘金網
通過linktext定位到標籤並點選。

後面通過div=juejin一層一層定位到input,最後點選進入文章。

認識html標籤

HTML <input>標籤

<input>標籤用於蒐集使用者資訊。
根據不同的 type 屬性值,輸入欄位擁有很多種形式。輸入欄位可以是文字欄位、核取方塊、掩碼後的文字控制元件、單選按鈕、按鈕等等。

<form action="form_action.asp" method="get">
  First name: <input type="text" name="fname" />
  Last name: <input type="text" name="lname" />
  <input type="submit" value="Submit" />
</form>
複製程式碼

詳情參考 http://www.w3school.com.cn/tags/tag_input.asp

HTML <a>標籤

<a> 標籤定義超連結,用於從一張頁面連結到另一張頁面。
<a> 元素最重要的屬性是 href 屬性,它指示連結的目標。
複製程式碼

詳情參考http://www.w3school.com.cn/tags/tag_a.asp

HTML <div>標籤

<div>可定義文件中的分割槽或節(division/section)。
<div>標籤可以把文件分割為獨立的、不同的部分。它可以用作嚴格的組織工具,並且不使用任何格式與其關聯。
如果用 id 或 class 來標記<div>,那麼該標籤的作用會變得更加有效。

<div style="color:#00FF00">
  <h3>This is a header</h3>
  <p>This is a paragraph.</p>
</div>
複製程式碼

詳情參考http://www.w3school.com.cn/tags/tag_div.asp

…………

其他標籤不一一介紹,可在參考網站上意義看

認識css

這裡只講1個關鍵的,比如

<div class="css1 css2"> ********</div>
複製程式碼

表示這個div同時使用了css1和css2樣式,只需要知道如果沒辦法在selenium上定位的這個div,可使用css名定位。

如果有興趣,可再看下其他css相關知識。

js基礎

這裡講2個關鍵

<a onclick="test()">test</a>
複製程式碼

上述程式碼,點選a標籤會執行js中的test方法,當selenium無法定位到這個a標籤,可以直接呼叫test()方法。

可以寫簡單的js指令碼,彈窗程式碼:

alert("hello");
複製程式碼

下載谷歌瀏覽器

下載谷歌瀏覽器,這裡可以使用63.0.3239.84版本。
目前來說,谷歌瀏覽器版本相容性還是不錯的。

下載selenium driver

https://www.seleniumhq.org/download/

可不下,本文github專案中包含

下載selenium webdriver

https://npm.taobao.org/mirrors/chromedriver/
需下載和谷歌瀏覽器對應的版本2.40
可不下,本文github專案中包含

下載idea開發工具

https://www.jetbrains.com/idea/

這個比較複雜,建議在java開發人員指導下完成。

selenium

這個版本是簡易版,但足夠

最終效果

自動化測試selenium在小公司的成功實踐

我們通過錄制selenium指令碼,編輯,提交到git庫,由jenkins自動化編譯出jar包,通過bat命令在任意一臺pc端執行(預設開發人員提交程式碼後自動執行所有模組)。按功能模組,測試專案,生成測試報告。對測試不通過的模組

最大化

driver.manage().window().maximize();
複製程式碼

開啟頁面

driver.get("https://www.baidu.com");
複製程式碼

定位元素

自動化測試selenium在小公司的成功實踐

多個相同時,返回第一個,沒有找到會拋異常NoSuchElementException

WebElement element = driver.findElement(*);
複製程式碼

當返回多個時:

List<WebElement> elements = driver.findElements(*);
複製程式碼

定位元素方式

<input class="input_class input_class2" type="text" name="user-name" id="user-id" /> 
複製程式碼

通過id定位

WebElement element = driver.findElement(By.id("user-id"));
複製程式碼

通過name定位

WebElement element = driver.findElement(By.name("user-name"));
複製程式碼

通過className定位

WebElement element = driver.findElement(By.className("input_class.input_class2"));
複製程式碼

注意多個class用小數點隔開,也可以使用cssSelector定位

WebElement element = driver.findElement(By.cssSelector("input"));
複製程式碼

通過linkText定位,如:

WebElement element = driver.findElement(By.linkText("我是如何重構整個研發專案,促進自動化運維DevOps的落地?"));
複製程式碼

意思就是連結內容定位

通過partialLinkText定位,模糊內容定位,和上相似

WebElement element = driver.findElement(By.linkText("我是如何重構整個研發專案?"));
複製程式碼

通過tagName定位

WebElement element = driver.findElement(By.tagName("form"));
複製程式碼

通過xpath定位

WebElement element = driver.findElement(By.xpath("//input[@id=`passwd-id`]")); 
複製程式碼

這個最為複雜,最簡單的版本是

//標籤型別[@屬性名=屬性值]
複製程式碼

但也可以定位第幾個

//input[4]
複製程式碼

其中[]中還可以增加邏輯and or表示式

WebElement element = driver.findElement(By.xpath("//input[@type=`text` and @name=`user-name`]"));
WebElement element = driver.findElement(By.xpath("//input[@type=`text` or @name=`user-name`]"));
複製程式碼

[]中也可以增加start-with、ends-with、contains,比如

WebElement element = driver.findElement(By.xpath("//input[start-with(@id,`user-`)]"));
WebElement element = driver.findElement(By.xpath("//input[ends-with(@id,`user-`)]"));
WebElement element = driver.findElement(By.xpath("//input[contains(@id,`user-`)]"));
複製程式碼

還可以 任意屬性名

WebElement element = driver.findElement(By.xpath("//input[@*=`user-name`]"));
複製程式碼

更多xpath使用方法見
http://www.w3school.com.cn/xpath/index.asp

單擊某個元素

.click()
複製程式碼

清空input

.clear();
複製程式碼

input中輸入內容

.sendKeys("掘金網");
複製程式碼

如果是上傳附件,可直接sendKeys路徑

.sendKeys("c:shao.png");
複製程式碼

得到input內容

.getText();
複製程式碼

下拉框

自動化測試selenium在小公司的成功實踐
    Select select = new Select(driver.findElement(By.id("frequency")));
    select.selectByValue("1");
    driver.findElement(By.id("validDays")).click();
    
複製程式碼
    select.selectByValue("a"); 
    select.deselectAll();
    select.deselectByValue("a");
    select.deselectByVisibleText("");
    select.getAllSelectedOptions();
    select.getFirstSelectedOption(); 
複製程式碼

單選框

WebElement radio=driver.findElement(By.id("radio"));
radio.click();&emsp;&emsp;&emsp;&emsp;   //選擇某個選項
radio.clear();&emsp;&emsp;&emsp;&emsp;  //清空選項
radio.isSelected();&emsp;&emsp;//判斷某個單選項是否被選中
複製程式碼

核取方塊

WebElement checkbox = driver.findElement(By.id("checkbox"));
checkbox.clear();       //清空選項
checkbox.isSelected(); //是否選中
複製程式碼

判斷是否可點選

isEnabled()
複製程式碼

alert框操作

Alert alert = driver.switchTo().alert();
alert.accept();&emsp;&emsp;//確定
alert.dismiss();&emsp; //取消
複製程式碼

iframe切換(重點

可能很多老的專案都有iframe,錄製指令碼的時候正常錄製,可執行的時候,卻無法執行,這個時候,需要切換iframe

driver.switchTo().defaultContent();&emsp;//回到預設的頁面
driver.switchTo().frame("leftFrame"); //切換到某個iframe
複製程式碼

切換iframe,結束後,記得切換回預設頁面。

        driver.findElement(By.linkText("匯入模板")).click();
        WebElement iframe = driver.findElement(By.id("layui-layer-iframe1"));
        driver.switchTo().frame(iframe);
        Thread.sleep(2000);
        driver.findElement(By.linkText("引用")).click();
        driver.findElement(By.xpath("//button[@type=`submit`]")).click();
        driver.findElement(By.xpath("(//button[@type=`button`])[3]")).click();
        Thread.sleep(1000);
        driver.findElement(By.linkText("學生")).click();
複製程式碼

以上摘自專案程式碼,僅供參考

執行 js

    JavascriptExecutor js = (JavascriptExecutor) driver;
    js.executeScript("viewDetail(`1f50555e409a4597a027ff415ce6c9b4`,`09`,`2018`)");
複製程式碼

執行內部viewDetail方法

延時操作(重要

很多時候我們需要延時,這時使用

Thread.sleep(1000);//延時1000毫秒
複製程式碼

許多錯誤是因為需要等待時間,嘗試增加一個延時,也許這個問題就過去了。

專案程式碼

假設,我們產品有多個環境,我們定義一個environments陣列,(當-1時,提示使用者輸入),有多個模組(當-1時,提示使用者輸入),最終程式碼如下,執行後,錯誤報告會通過郵件傳送到指定郵箱或者其他地方。

執行效果圖

自動化測試selenium在小公司的成功實踐
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import webfunction.*;

import java.util.Scanner;

public class Main {
    private static WebDriver driver;
    private static String baseUrl;
    private boolean acceptNextAlert = true;
    /**
     * 各個環境
     * */
    private static String[] environments = {"環境1", "環境2", "環境3", "環境4", "環境5", "環境6"};
    /**
     * 錯誤日誌
     * */
    private static StringBuffer verificationErrors = new StringBuffer();
    /**
     * 是否處於debug模式
     */
    private static boolean debug = false;
    /**
     * -1為手動模式,否則為指定數字
     * */
    private static String environment = "-1";
    /**
     * -1為手動模式,否則為指定數字
     * */
    private static String methods = "-1";

    public static void main(String[] args) throws Exception {
        //引用火狐瀏覽器驅動
        System.setProperty("webdriver.chrome.driver", "C:\selenium\chromedriver.exe");

        //定義使用者名稱密碼
        String uname, upw;
        Scanner sc = new Scanner(System.in);
        System.out.println("請選擇環境");
        for (int i = 0; i < environments.length; i++) {
            System.out.println(i + ":" + environments[i]);
        }
        if ("-1".equals(environment)) {
            environment = sc.next();
        }

        System.out.println("請輸入需要測試的功能,英文逗號隔開");
        if ("-1".equals(methods)) {
            methods = sc.next();
        }
        driver = new ChromeDriver();

        System.out.println("您選擇的是" + environments[Integer.valueOf(environment)]);
        switch (environment) {
            case "0":
                baseUrl = "http://*.*.*.*/";
                uname = "admin";
                upw = "admin";
                testManage(baseUrl, uname, upw, methods, driver);
                break;
            case "1":
                baseUrl = "http://*.*.*.*/";
                uname = "admin";
                upw = "admin";
                testManage(baseUrl, uname, upw, methods, driver);
                break;
            case "2":
                //等等等……
                break;
        }

    }

    private static void testManage(String url, String uname, String upw, String methods, WebDriver driver) throws InterruptedException {
        //先登入管理端
        WebLogin.webLogin(driver, url, uname, upw);
        //然後測試所有模組
        String[] strArray = null;
        strArray = methods.split(",");
        for (int i = 0; i < strArray.length; i++) {
            switch (strArray[i]) {
                case "0":
                    try {
                        // 系統基礎管理 - 使用者管理 - 新增使用者
                        WebSystemManage.addnewUser(driver, url);     
                    } catch (Exception e) {
                        verificationErrors.append("系統基礎管理 - 使用者管理 - 新增使用者   出錯");
                        log(e);
                    }
                    break;
                case "1":
                    try {
                        // 系統基礎管理 - 使用者管理 - 編輯使用者
                        WebSystemManage.editUser(driver, url);       
                    } catch (Exception e) {
                        System.out.println("系統基礎管理 - 使用者管理 - 編輯使用者   出錯");
                        log(e);
                    }
                    break;
                default:
                    break;
            }
        }
        report(verificationErrors);
    }

    private static void report(StringBuffer verificationErrors) {
        //傳送郵件
    }

    /**
     * 根據debug變數是否輸出日誌
     * @param e
     */
    private static void log(Exception e) {
        if (debug) {
            e.printStackTrace();
        }
    }


    private static boolean isElementPresent(By by) {
        try {
            driver.findElement(by);
            return true;
        } catch (NoSuchElementException e) {
            return false;
        }
    }

    private static boolean isAlertPresent() {
        try {
            driver.switchTo().alert();
            return true;
        } catch (NoAlertPresentException e) {
            return false;
        }
    }

    private static String closeAlertAndGetItsText() {
        try {
            Alert alert = driver.switchTo().alert();
            String alertText = alert.getText();
            if (acceptNextAlert) {
                alert.accept();
            } else {
                alert.dismiss();
            }
            return alertText;
        } finally {
            acceptNextAlert = true;
        }
    }
}

複製程式碼

程式碼那麼多其實我們只關注 public static void main(String[] args) throws Exception {}內的內容,比如,我們想執行我們最初錄製的掘金指令碼,只需將那端我要求特別關注的程式碼放到裡面即可,具體程式碼如下:

import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;

public class Main {
    private static WebDriver driver;

    public static void main(String[] args) throws Exception {
        //引用火狐瀏覽器驅動
        System.setProperty("webdriver.chrome.driver", "C:\selenium\chromedriver.exe");
        driver = new ChromeDriver();
        driver.manage().window().maximize();
        //以下為Katalon Recorder錄製後的指令碼
        driver.get("https://www.baidu.com/index.php?tn=monline_3_dg");
        Thread.sleep(2000);
        driver.findElement(By.id("kw")).click();
        driver.findElement(By.id("kw")).clear();
        driver.findElement(By.id("kw")).sendKeys("掘金網");
        Thread.sleep(100);
        driver.findElement(By.id("su")).click();
        Thread.sleep(1000);
        driver.findElement(By.xpath("//div[@id=`container`]/div[2]/div")).click();
        driver.findElement(By.linkText("掘金- juejin.im - 一個幫助開發者成長的社群")).click();
        Thread.sleep(3000);
        // ERROR: Caught exception [ERROR: Unsupported command [selectWindow | win_ser_1 | ]]
        driver.findElement(By.xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input")).click();
        driver.findElement(By.xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input")).click();
        driver.findElement(By.xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input")).clear();
        driver.findElement(By.xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input")).sendKeys("我是如何重構整個研發專案,促進自動化運維DevOps的落地?");
        driver.findElement(By.xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input")).sendKeys(Keys.ENTER);
        Thread.sleep(2000);
        driver.findElement(By.linkText("我是如何重構整個研發專案,促進自動化運維DevOps的落地?")).click();
    }
}

複製程式碼

上述程式碼中註釋內是Katalon Recorder匯出的指令碼,但是我們增加了一些延時操作,selenium延時有很3種:普通sleep、顯示等待方式、隱式等待方式。這裡先簡單粗暴一下,用Thread.sleep(*);延時,比如開啟百度延時2秒、輸入“掘金網”延時100毫秒、搜尋後延時3秒…………

很遺憾,我們程式碼報錯:

自動化測試selenium在小公司的成功實踐

大概意思說超時沒有找到那個搜尋框,由於各種各樣的原因,會導致我們在火狐瀏覽器中錄製的指令碼在java程式碼中的谷歌瀏覽器裡無法相容,這個時候我們需要去分析一下具體邏輯。

這裡是由於新視窗需要切換window,可使用下述程式碼切換(替換程式碼中// ERROR: Caught exception [ERROR: Unsupported command [selectWindow | win_ser_1 | ]]這行即可)。

        Set<String> windowHandles = driver.getWindowHandles();
        String windowHandle = driver.getWindowHandle();
        for (String handle : windowHandles) {
            if (!handle.equals(driver.getWindowHandle())) {
                driver.switchTo().window(handle);
                break;
            }
        }
複製程式碼

匯出的指令碼By.xpath(“//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input”)這一句很複雜,我們試著簡化它。

自動化測試selenium在小公司的成功實踐
<input data-v-5ce25e66="" maxlength="32" placeholder="搜尋掘金" class="search-input">
複製程式碼

首先搜尋下search-input樣式,看該頁面是否只有一個search-input樣式。

自動化測試selenium在小公司的成功實踐

果然search-input樣式只有一個標籤。

於是我們將

By.xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input")
//改為
By.className("search-input")
複製程式碼

最終程式碼

import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;

import java.util.Set;

public class Main {
    private static WebDriver driver;

    public static void main(String[] args) throws Exception {
        //引用火狐瀏覽器驅動
        System.setProperty("webdriver.chrome.driver", "C:\selenium\chromedriver.exe");
        driver = new ChromeDriver();
        driver.manage().window().maximize();
        driver.get("https://www.baidu.com/index.php?tn=monline_3_dg");
        Thread.sleep(2000);
        driver.findElement(By.id("kw")).click();
        driver.findElement(By.id("kw")).clear();
        driver.findElement(By.id("kw")).sendKeys("掘金網");
        Thread.sleep(100);
        driver.findElement(By.id("su")).click();
        Thread.sleep(1000);
        driver.findElement(By.xpath("//div[@id=`container`]/div[2]/div")).click();
        driver.findElement(By.linkText("掘金- juejin.im - 一個幫助開發者成長的社群")).click();
        Thread.sleep(7000);
        Set<String> windowHandles = driver.getWindowHandles();
        String windowHandle = driver.getWindowHandle();
        for (String handle : windowHandles) {
            if (!handle.equals(driver.getWindowHandle())) {
                driver.switchTo().window(handle);
                break;
            }
        }
        // ERROR: Caught exception [ERROR: Unsupported command [selectWindow | win_ser_1 | ]]
        driver.findElement(By.xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input")).click();
        driver.findElement(By.xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input")).click();
        driver.findElement(By.xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input")).clear();
        driver.findElement(By.xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input")).sendKeys("我是如何重構整個研發專案,促進自動化運維DevOps的落地?");
        driver.findElement(By.xpath("//div[@id=`juejin`]/div[2]/div/header/div/nav/ul/li[2]/form/input")).sendKeys(Keys.ENTER);
        Thread.sleep(2000);
        driver.findElement(By.linkText("我是如何重構整個研發專案,促進自動化運維DevOps的落地?")).click();
    }
}
複製程式碼

編譯打包

自動化測試selenium在小公司的成功實踐
自動化測試selenium在小公司的成功實踐

得到selenium.jar包,可複製到C:selenium下,和chromedriver.exe同級。

自動化測試selenium在小公司的成功實踐

輸入cmd命令

C:UsersAdministrator>cd C:selenium

C:selenium>java -jar selenium2.jar
複製程式碼

即可自動化執行,非windows系統下載2.40其他版本https://npm.taobao.org/mirrors/chromedriver/2.40/

自動化測試selenium在小公司的成功實踐

github專案執行

https://github.com/qq273681448/selenium

為了防止有讀者沒有改maven庫映象,所以把lib包都放在專案中了。直接使用idea開啟,可能有些配置需要改,可參考

自動化測試selenium在小公司的成功實踐
自動化測試selenium在小公司的成功實踐
自動化測試selenium在小公司的成功實踐
自動化測試selenium在小公司的成功實踐
自動化測試selenium在小公司的成功實踐

寫在最後

至此,一個基礎版的selenium框架就搭好了,後續,可以連線資料庫,從庫中隨機取出帳號,進行專案測試。也可以配合bat指令碼,實現自動化測試以及報告生成。

相關文章