簡單介紹Selenium Grid

Posted by Elizabeth Huang on Wed, Sep 21, 2022

用途

Selenium Grid可以讓一個測試在不同機器上的瀏覽器執行測試

一個測試發到Hub,然後發給適合的Node執行。每個Node可以是不同作業系統、不同瀏覽器,瀏覽器也可以是不同版本

架構
圖片來源

組件

官方文件中有一張組件圖

組件圖

先了解WebDriver session定義,再看組件會比較清楚~ W3C的文件中,寫道下面這句:

A WebDriver session represents the connection between a local end and a specific remote end.(https://w3c.github.io/webdriver/#dfn-session) 用中文簡單來說,就是本地端和特定遠端之間的連接

Selenium Grid擁有六種組件:

  • Node:執行session,一個grid可有多個nodes,每個node可以是不同的作業系統
  • Session Queue:以queue的形式維護session
  • Distributor:將session分配給合適的Node執行
  • Session Map:紀錄session id和Node的關係
  • Event Bus:負責以上四種組件的溝通
  • Router:接收所有請求並發給組件

當Router收到一個新的session request,它將被轉發到Session Queue

之後Distributor會從Session Queue取得session然後發給適合的Node執行,然後在Session Map紀錄sessionID和執行的Node

如果是已存在的session,Router會查詢Session Map取得正在運行session的Node ID,然後將request直接轉發到 Node,不會再轉給Session Queue。

Grid Roles

有三種不同的部屬方式,可以根據自身的需要選擇合適的方式

  • Standalone:全部組件在同一台機器
  • Hub and Node:除了Node,其餘組件組成Hub,Hub和Node各自啟動,可以是不同機器
  • Distributed:每個組件各自啟動,可以是不同機器

使用Docker啟動Selenium grid

開發環境非常適合使用Docker啟動環境,這個Docker Selenuim可以非常方便的用compose.yaml一次啟動Hub和多個不同瀏覽器的Node

compose.yaml

 1version: "3"
 2services:
 3  chrome:
 4    image: selenium/node-chrome:latest
 5    shm_size: 2gb
 6    depends_on:
 7      - selenium-hub
 8    environment:
 9      - SE_EVENT_BUS_HOST=selenium-hub
10      - SE_EVENT_BUS_PUBLISH_PORT=4442
11      - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
12    ports:
13      - 7900:7900
14
15  edge:
16    image: selenium/node-edge:latest
17    shm_size: 2gb
18    depends_on:
19      - selenium-hub
20    environment:
21      - SE_EVENT_BUS_HOST=selenium-hub
22      - SE_EVENT_BUS_PUBLISH_PORT=4442
23      - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
24    ports:
25      - 7901:7900
26
27  firefox:
28    image: selenium/node-firefox:latest
29    shm_size: 2gb
30    depends_on:
31      - selenium-hub
32    environment:
33      - SE_EVENT_BUS_HOST=selenium-hub
34      - SE_EVENT_BUS_PUBLISH_PORT=4442
35      - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
36    ports:
37      - 7902:7900
38
39  selenium-hub:
40    image: selenium/hub:latest
41    container_name: selenium-hub
42    ports:
43      - "4442:4442"
44      - "4443:4443"
45      - "4444:4444"

Hub開啟4444port,到時候測試用4444port跟grid連接。4442和4443port是給Event Bus使用,一個是發佈、一個是訂閱

如果需要監看瀏覽器操作流程,就要開啟port對應node 7900port。上述的compose.yaml中,chrome開啟7900,edge開啟7901等。如果不需要監看就不必開啟

docker-compose up –d起環境以後,進入 http://localhost:4444/ui 檢查狀態

檢查狀態

也可以curl GET 'http://localhost:4444/status'檢查狀態

寫一個測試

這裡拿上一篇文章的測試,不過這次我改用pytest,因為比較方便!

這個測試用fixture建立一個chrome webdriver,打開鉅亨網,然後將游標懸停在導航欄的新聞上,最後assert在頁面上看到「台股盤勢」四個字

 1import pytest
 2from selenium import webdriver
 3from selenium.webdriver import ActionChains
 4from selenium.webdriver.common.by import By
 5
 6
 7@pytest.fixture(scope="module")
 8def driver():
 9    driver = webdriver.Chrome()
10    driver.maximize_window()
11    yield driver
12    driver.quit()
13
14def test_search_in_python_org(driver):
15    driver.get("https://www.cnyes.com/")
16    elem = driver.find_element(
17        by=By.CSS_SELECTOR,
18        value='nav ul li a[data-global-ga-label="新聞"]>span'
19    )
20    ActionChains(driver).move_to_element(elem).perform()
21    assert "台股盤勢" in driver.page_source

現在搭配Selenium grid,webdriver必須改為remote,因為要連接Hub

1@pytest.fixture(scope="module")
2def driver():
3    driver = webdriver.Remote(
4        command_executor='http://localhost:4444',
5        options=webdriver.ChromeOptions()
6    )
7    driver.maximize_window()
8    yield driver
9    driver.quit()

修改後,我們來監看瀏覽器的操作狀況。在compose.yaml有映射7900 port,所以連接到http://localhost:7900/,可以看到這個畫面

noVNC
點擊「連線」後,輸入密碼(預設密碼是secret) 回到command line,就可以開始測試,並且監看操作狀況啦!

1pytest

在不同的瀏覽器測試

在上面的測試裡,只用了Chrome瀏覽器測試。如果想要在一個測試中指定多種瀏覽器的option,可以使用pytest parametrizing fixtures

 1def create_options(browser_name):
 2    if browser_name == 'firefox':
 3        options = webdriver.FirefoxOptions()
 4    elif browser_name == 'edge':
 5        options = webdriver.EdgeOptions()
 6    else:
 7        options = webdriver.ChromeOptions()
 8    return options
 9
10@pytest.fixture(scope="module", params=['chrome', 'edge', 'firefox'])
11def driver(request):
12    driver = webdriver.Remote(
13        command_executor='http://localhost:4444',
14        options=create_options(request.param)
15    )
16    driver.maximize_window()
17    yield driver
18    driver.quit()

在fixture加上params參數,是測試的瀏覽器list。當執行driver這個function時,request.param會按照順序取得chrome, edge, firefox。又新增了create_options function,依照傳入的瀏覽器名稱回傳options

並行測試

可以使用pytest-xdist,讓它用多個CPU加速處理多個測試

1pytest –n 3

參考

範例Code

我的Repo