• 端对端 自动化单元测试


    端对端 自动化单元测试

    1. W3C WebDriver 标准
    2. 自动化启动浏览器、开启Tab页
      1. 测试单元用例
    3. 输出结果

    浏览器自动化提要

    W3C WebDriver API. 标准

    浏览器 OpenApi : https://www.w3.org/TR/webdriver1/#list-of-endpoints

    各浏览器 OpenApi 的实现

    以下为各厂家对于浏览器 OpenApi的实现

    https://www.selenium.dev/documentation/webdriver/getting_started/install_drivers/

    BrowserSupported OSMaintained byDownloadIssue Tracker
    Chromium/ChromeWindows/macOS/LinuxGoogleDownloadsIssues
    FirefoxWindows/macOS/LinuxMozillaDownloadsIssues
    EdgeWindows/macOSMicrosoftDownloadsIssues
    Internet ExplorerWindowsSelenium ProjectDownloadsIssues
    SafarimacOS High Sierra and newerAppleBuilt inIssues

    Chrome 为例

    针对 不同的版本,具有不同的浏览器驱动,需要查看当前浏览器版本,再下载对应版本的驱动

    如: 当前 Chrome 已是最新版本 版本 104.0.5112.101(正式版本) (arm64)

    从下载地址(https://chromedriver.chromium.org/downloads)

    下载对应一下版本号的的驱动即可

    JS 模块 抹平各浏览的差异

    JS模块是对于Api的使用且已经抹平各浏览器厂家API的差异

    webdriver

    https://www.npmjs.com/package/webdriver

    npm i webdriver
    
    • 1

    WebdriverIO

    https://www.npmjs.com/package/webdriverio

    npm install webdriverio
    
    • 1

    selenium-webdriver

    https://www.npmjs.com/package/selenium-webdriver

    # 安装JS驱动
    npm install selenium-webdriver
    
    • 1
    • 2

    WebBriver 服务

    本地服务

    const { Builder } = require('selenium-webdriver');
    const chrome = require('selenium-webdriver/chrome');
    
    /**
     * 获取驱动服务
     * 浏览器驱动启动后,会挂载到一个端口上我们可以通过HTTP服务访问、操作浏览器
     */
    const service = new chrome.ServiceBuilder('/Users/shang/Code/github/webdriver/chromedriver');
    // 获取Chrome浏览器配置
    const options = new chrome.Options();
    // 浏览器参数设置
    const arguments = [
      '--disable-web-security', // 关闭浏览器安全设置,如:浏览器跨域
      '--incognito',            // 使用隐身模式
      '--user-data-dir=/Users/shang/Downloads/chromeincognito' // 设置当前用户的工作目录
    ]
    options.addArguments.apply(options, arguments);
    
    (async () => {
    
    const driver = await new Builder()
      .setChromeOptions(options)
      .forBrowser('chrome')
      .setChromeService(service)
      .build();
    
    driver.getSession()
    await driver.manage().setTimeouts({ implicit: 500 });
    
    // 退出浏览器
    // driver.quit()
    
    })();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    远端服务

    启动远端服务

    ➜  webdriver ls
    chromedriver      index.js          node_modules      package-lock.json package.json
    ➜  webdriver ./chromedriver
    Starting ChromeDriver 104.0.5112.79 (3cf3e8c8a07d104b9e1260c910efb8f383285dc5-refs/branch-heads/5112@{#1307}) on port 9515
    Only local connections are allowed.
    Please see https://chromedriver.chromium.org/security-considerations for suggestions on keeping ChromeDriver safe.
    ChromeDriver was started successfully.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    添加IP白名单

    ➜  webdriver ./chromedriver --allowed-ips='0.0.0.0'
    Starting ChromeDriver 104.0.5112.79 (3cf3e8c8a07d104b9e1260c910efb8f383285dc5-refs/branch-heads/5112@{#1307}) on port 9515
    Remote connections are allowed by an allowlist (0.0.0.0).
    Please see https://chromedriver.chromium.org/security-considerations for suggestions on keeping ChromeDriver safe.
    ChromeDriver was started successfully.
    
    • 1
    • 2
    • 3
    • 4
    • 5

    其他配置

    ➜  webdriver ./chromedriver --help
    Usage: ./chromedriver [OPTIONS]
    
    Options
      --port=PORT                     port to listen on
      --adb-port=PORT                 adb server port
      --log-path=FILE                 write server log to file instead of stderr, increases log level to INFO
      --log-level=LEVEL               set log level: ALL, DEBUG, INFO, WARNING, SEVERE, OFF
      --verbose                       log verbosely (equivalent to --log-level=ALL)
      --silent                        log nothing (equivalent to --log-level=OFF)
      --append-log                    append log file instead of rewriting
      --replayable                    (experimental) log verbosely and don't truncate long strings so that the log can be replayed.
      --version                       print the version number and exit
      --url-base                      base URL path prefix for commands, e.g. wd/url
      --readable-timestamp            add readable timestamps to log
      --enable-chrome-logs            show logs from the browser (overrides other logging options)
      --allowed-ips=LIST              comma-separated allowlist of remote IP addresses which are allowed to connect to ChromeDriver
      --allowed-origins=LIST          comma-separated allowlist of request origins which are allowed to connect to ChromeDriver. Using `*` to allow any host origin is dangerous!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    调用远端服务

    const { Builder } = require('selenium-webdriver');
    const chrome = require('selenium-webdriver/chrome');
    
    /**
     * 获取驱动服务
     * 浏览器驱动启动后,会挂载到一个端口上我们可以通过HTTP服务访问、操作浏览器
     */
    // const service = new chrome.ServiceBuilder('/Users/shang/Code/github/webdriver/chromedriver');
    // 获取Chrome浏览器配置
    const options = new chrome.Options();
    // 浏览器参数设置
    const arguments = [
      '--disable-web-security', // 关闭浏览器安全设置,如:浏览器跨域
      '--incognito',            // 使用隐身模式
      '--user-data-dir=/Users/shang/Downloads/chromeincognito' // 设置当前用户的工作目录
    ]
    options.addArguments.apply(options, arguments);
    
    (async () => {
    
    const driver = await new Builder()
      .usingServer("http://127.0.0.1:9515")
      .setChromeOptions(options)
      .forBrowser('chrome')
    //  .setChromeService(service)
      .build();
    
    driver.getSession()
    await driver.manage().setTimeouts({ implicit: 500 });
    
    // 退出浏览器
    // driver.quit()
    
    })();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    配置端到端准备

    以 selenium-webdriver 为例

    安装依赖

    浏览器安装

    略过,chrome , Firefox, Edge

    安装浏览器驱动

    根据浏览器下载驱动

    Chrome 安装

    下载地址: https://chromedriver.chromium.org/downloads

    104 版本平台区分

    Linux: https://chromedriver.storage.googleapis.com/104.0.5112.79/chromedriver_linux64.zip

    Mac64: https://chromedriver.storage.googleapis.com/104.0.5112.79/chromedriver_mac64.zip

    Mac64M1: https://chromedriver.storage.googleapis.com/104.0.5112.79/chromedriver_mac64_m1.zip

    配置

    # 创建工作目录
    npm inint -y 
    # 安装 selenium-webdriver ,当前为 4.4.0 版本
    npm install --save-dev selenium-webdriver
    
    • 1
    • 2
    • 3
    • 4

    示例:启动浏览器

    Chrome 浏览器相关的参数,

    https://blog.csdn.net/swe_ling/article/details/125811499?spm=1001.2014.3001.5502

    const { Builder } = require('selenium-webdriver');
    const chrome = require('selenium-webdriver/chrome');
    
    /**
     * 获取驱动服务
     * 浏览器驱动启动后,会挂载到一个端口上我们可以通过HTTP服务访问、操作浏览器
     */
    const service = new chrome.ServiceBuilder('/Users/shang/Code/github/webdriver/chromedriver');
    // 获取Chrome浏览器配置
    const options = new chrome.Options();
    // 浏览器参数设置
    const arguments = [
      '--disable-web-security', // 关闭浏览器安全设置,如:浏览器跨域
      '--incognito',            // 使用隐身模式
      '--user-data-dir=/Users/shang/Downloads/chromeincognito' // 设置当前用户的工作目录
    ]
    options.addArguments.apply(options, arguments);
    
    (async () => {
    
    const driver = await new Builder()
      .setChromeOptions(options)
      .forBrowser('chrome')
      .setChromeService(service)
      .build();
    
    driver.getSession()
    await driver.manage().setTimeouts({ implicit: 500 });
    
    // 退出浏览器
    // driver.quit()
    
    })();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    功能操作

    打开浏览器

    打开浏览器或者 Tab 页

    https://www.selenium.dev/documentation/webdriver/browser/windows/

    driver.switchTo().newWindow()
    
    • 1

    创建多个Window

    // 打开并切换到新的窗口
    const windowHandle = await driver.switchTo().newWindow('window');
    await driver.navigate().to('http://0.0.0.0:80') 
    // 打开并切换到新的窗口
    const otherWindowHandle = await driver.switchTo().newWindow('window');
    await driver.navigate().to('http://0.0.0.0:8080')
    ... ...
    
    // 切换到 windowHandle 窗口
    driver.switchTo().window(windowHandle);
    // 切换到 otherWindowHandle 窗口
    driver.switchTo().window(otherWindowHandle);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    多个Tab页操作

    创建新的Tab页,打开指定链接,并返回该Tab页的操作 句柄 (Tab标识符)

    https://www.selenium.dev/documentation/webdriver/browser/windows/#switching-windows-or-tabs

    async function getTap (url) {
      await driver.switchTo().newWindow()
      await driver.navigate().to(url)
      const qqlWindowHandle = await driver.getWindowHandle();
      return qqlWindowHandle
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    完整示例

    // index.js 
    
    const { Builder } = require('selenium-webdriver');
    const chrome = require('selenium-webdriver/chrome');
    
    /**
     * 获取驱动服务
     * 浏览器驱动启动后,会挂载到一个端口上我们可以通过HTTP服务访问、操作浏览器
     */
    const service = new chrome.ServiceBuilder('/Users/shang/Code/github/webdriver/chromedriver');
    // 获取Chrome浏览器配置
    const options = new chrome.Options();
    
    options.setChromeLogFile('/Users/shang/Downloads/chrome.log');
    // 
    const arguments = [
      '--disable-web-security', // 关闭浏览器安全设置,如:浏览器跨域
      // '--incognito', // 使用隐身模式
      // '--user-data-dir=/Users/shang/Downloads/chromeincognito' // 设置当前用户的工作目录
    ]
    options.addArguments.apply(options, arguments);
    
    (async () => {
    
    // 创建一个浏览器驱动
    const driver = await new Builder()
      .setChromeOptions(options)
      .forBrowser('chrome')
      .setChromeService(service)
      .build()
    
    async function getTap (url) {
      await driver.switchTo().newWindow()
      await driver.navigate().to(url)
      const qqlWindowHandle = await driver.getWindowHandle();
      return qqlWindowHandle
    }
    
    const qqWindowHandle = await getTap('https://www.qq.com')
    const baiduWindowHandle = await getTap('https://www.baidu.com')
    
    // 等待2秒
    await new Promise((resolve) => { setTimeout(resolve, 2000) })
    // 切换到 QQ 所在窗口
    await driver.switchTo().window(qqWindowHandle);
    // 执行JavaScript方法 所在窗口
    await driver.executeScript(function (handle) {
      console.log('output handle qq', handle, window.document.title)
    }, qqWindowHandle)
    
    // 等待2秒
    await new Promise((resolve) => { setTimeout(resolve, 2000) })
    // 切换到 百度 所在窗口
    await driver.switchTo().window(baiduWindowHandle);
    // 执行JavaScript方法 所在窗口
    await driver.executeScript(function (handle) {
      console.log('output handle baidu', handle, window.document.title)
    }, qqWindowHandle)
    
    await new Promise((resolve) => { setTimeout(resolve, 2000) })
    
    // 退出浏览器
    driver.quit()
    
    })();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    执行测试任务

    node index.js
    
    • 1

    示例日志结果

    [12369:259:0822/115042.778808:INFO:CONSOLE(306)] "每一个星球都有一个驱动核心,
    每一种思想都有影响力的种子。
    感受世界的温度,
    年轻的你也能成为改变世界的动力,
    百度珍惜你所有的潜力。
    你的潜力,是改变世界的动力!
    
    ", source: https://pss.bdstatic.com/r/www/cache/static/protocol/https/global/js/all_async_search_098edf7.js (306)
    [12369:259:0822/115042.778926:INFO:CONSOLE(306)] "%c百度2022校园招聘简历投递:https://talent.baidu.com/external/baidu/campus.html color:red", source: https://pss.bdstatic.com/r/www/cache/static/protocol/https/global/js/all_async_search_098edf7.js (306)
    [12369:259:0822/115046.448069:INFO:CONSOLE(4)] "output handle qq CDwindow-2F3C312D42DF8A638BA8C5322A85B541 腾讯首页", source:  (4)
    [12369:259:0822/115049.493448:INFO:CONSOLE(4)] "output handle baidu CDwindow-2F3C312D42DF8A638BA8C5322A85B541 百度一下,你就知道", source:  (4)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    抛砖引玉

    实现自动化测试

    1. 把要使用的方法通过 Demo 挂载到全局。

    2. 实现多用户多场景登录。

      1. 多tab页,
      2. 多window窗口,
      3. 多浏览器,
      4. 多设备,[ WebDriver Remote Server ]
    3. 通过调用 Demo 上挂载的 全局变量 实现具体的功能操作。

  • 相关阅读:
    selenium 下载文件取消安全下载的方法
    Java程序设计2023-第六次上机测试
    【Java】Java生成PDF工具类
    Spring Cloud Alibaba —— 服务注册与配置中心
    2022-08-01
    趣学算法 —— 兔子数列
    【算法】二分查找-20231120
    【题解】Educational Codeforces Round 142(CF1792)
    php实现普通和定时跳转的几种方式
    [附源码]计算机毕业设计JAVA大学生足球预约信息
  • 原文地址:https://blog.csdn.net/swe_ling/article/details/126463729