selenium結合docker構建分佈式測試環境

http://www.lancoude.com/16054602.html


selenium是目前web和app自動化測試的主要框架。對於web自動化測試而言,由於selenium2.0以後socker服務器由本地瀏覽器自己啓動且直接通過瀏覽器原生API操作頁面,故越來越多的人不再使用selenium RC了。大家使用的大多數是selenium-client,python版本的selenium-client最新版本是3.3.3(2017-04-04發佈),卻忽略了selenium server!事實上在大型的Grid分佈式佈局中必須要使用selenium server,我也會對這個佈局的使用做一些必要的解釋。在開始這個話題之前我說明兩點內容:

第一、寫selenium結合docker的使用只是個縮影,其實我想讓大家瞭解使用docker,你可以不精通但是你得會用!!!

第二、我不太想寫一些關於selenium的一些基礎操作,如:browser.get(×××),browser.find_element_by_id(×××)....這些大家都會,不會可以查一些文檔都會有相應的解釋,對我自己而言我想記錄的是一些關於比較高級點的話題,或者在自動化過程中遇到的難題(比如上篇操作flash),又或者selenium中大家忽略的一些方法的使用。

廢話說了不少,開始我們的話題selenium結合docker構建分佈式測試環境。

1.瞭解Selenium Standalone Server的使用

Selenium Standalone Server目前最新的版本是3.3.1(dcoker中selenium/hub這個鏡像下載的也是這個最新的版本),因爲我們的分佈式測試環境是基於這個包的使用,我們花一點時間來說說如何使用它。下面是一張Grid分佈式佈局的基本結構:

selenium結合docker構建分佈式測試環境

上圖中的selenium hub類似於一箇中央處理器,selenium node是遠程需要執行測試的節點,selenium Test case是運行在hub上的測試用例。

如何啓動一個selenium hub?

首先需要java的運行環境,在終端中輸入java -jar selenium-server-standalone-x.xx.x.jar -role hub -參數。

關於這個啓動的參數其實是需要我們來關注的。我們輸入java -jar selenium-server-standalone-2.52.0.jar -role hub -help下面列出來很多啓動參數,我相信你應該能看得懂,在下面的介紹中我也會提到一些參數。

如何向selenium hub中註冊信息?

在終端中輸入java -jar selenium-server-standalone-x.xx.x.jar -role node -參數。

同理,我們輸入java -jar selenium-server-standalone-2.52.0.jar -role node -help獲取一些啓動node的參數說明。

2.瞭解docker

說完了Grid的一些原理,我們來看看docker關於它的說明自行百度。我對它的簡單理解如下:

docker就像個貨船,貨船是幹嘛的?裝集裝箱的! 那麼這些集裝箱是什麼?集裝箱是一個個小的容器! 容器是什麼? 容器就是一個個搭建有簡易linux系統的特定應用,比如這個容器只搭建了java環境或者只搭建了apache環境! 容器如何啓動它? 通過鏡像來啓動! 鏡像怎麼來的? 通過從鏡像源pull或者自己構建鏡像!

自言自語一會,不知道你是否明白我說什麼了,不明白也沒關係,我們結合搭建分佈式環境這個應用,你應該會明白!

說完了docker是什麼,我們着手使用!

1.如何安裝docker?

如何安裝docker百度上有很多它的說明,由於我的是windows系統,關於它的安裝使用推薦文章如下:

http://www.jianshu.com/p/4052926bc12c這篇文章講述了用docker搭建spalsh服務器,裏面有詳細的關於docker的安裝使用

2.如何獲取鏡像?

我們用xshell或者CRT連接docker後,執行如下命令:

docker pull selenium/hub,它會自動從鏡像源中下載最新的selenium/hub鏡像

docker pull selenium/node-firefox,它會自動從鏡像源中下載最新selenium/node-firefox鏡像

當然可以用docker pull selenium/node-chrome下載selenium/node-chrome的鏡像,目前是沒有基於IE或者Safari瀏覽器的鏡像,當然我們完全有能力去自己構建這些鏡像!這一篇我主要講基於firefox瀏覽器的,其他的類似...

順便提一句,可以利用docker search+鏡像名稱能搜索鏡像源所存在的鏡像名稱。當我們下載完這2個鏡像後,我們在Xshell中輸入docker images應該有如下信息:

selenium結合docker構建分佈式測試環境

3.如何啓動這2個鏡像?

上文提到了我們先啓動一個hub,docker啓動命令如下:

Sikilu$ docker run -p 5555:4444 -d --name 'selenium_hub'  selenium/hub做一些簡單的說明:

run:通過鏡像啓動一個容器

-p:端口映射,5555是容器宿主機的端口就是我們docker這個輪船的端口,4444是我們容器的端口就是我們集裝箱的端口。這說明了我們把容器的4444端口開放給docker主機的5555端口,那麼我們就可以通過docker主機的5555端口來訪問容器了,有點囉嗦~~~

-d:docker後臺運行這個容器,我們知道運行server-standalone-2.52.0.jar這個包實際上是啓動一個socket程序的,是在一個while循環中的。如果不啓用後臺運行的話,在xshell當前窗口是不能進行其他的操作的,當然你要再開一個窗口連接docker也可以。

--name:指定容器運行的別名,如果不指定會隨機生成一個。

selenium/hub:就是我們要運行的鏡像文件。

啓動完hub後,我們啓動一個node,啓動node命令如下:

docker run -P -d --link selenium_hub:hub  selenium/node-firefox做一些簡單的說明:

run:和上文相同

-P:隨機生成映射端口號,上文中的-p是指定特定的端口號,這裏面是node我們並不需要知道容器內部的端口號,當然你要指定也可以,端口號不要衝突即可。

-d:後臺運行與上文相同。

--link:說明我們這個容器是依賴上文中我們生成的容器selenium_hub,後面我們會提到link的使用。

selenium_hub:hub:前面的selenium_hub是我們上文中通過selenium/hub鏡像啓動容器的別名;後面的hub一定要寫成hub或者HUB,寫成其他啓動失敗,爲什麼這樣我們後面會和--link一起說明。

selenium/node-firefox:node的鏡像。

啓動了selenium/hub與selenium/node後,我們運行docker ps有如下信息:

selenium結合docker構建分佈式測試環境

我們看出來相應的一些啓動信息。

CONTAINER ID:容器的id號,隨機生成,我們可以對他進行docker stop +id號、docker start +id號,或者docker rm+id號(刪除該容器)

IMAGE:我們利用到的鏡像文件。

CMMAND:容器啓動時執行的命令,有Dockerfile文件指定,後面提到!

PORTS:容器的一些端口信息。

NAMES:注意到,在啓動node時我們沒有指定--name屬性,所以他隨機命名了sleepy_bhabha,而啓動hub我們指定了--name屬性,hub容器的name爲我們指定的selenium-hub

由於我們是在後臺啓動這2個鏡像文件的,我們想要看容器啓動的log怎麼辦,以selenium_hub爲例子,我們輸入docker logs selenium_hub有如下信息:

selenium結合docker構建分佈式測試環境

這些信息與我們在本地PC機上使用selenium-server-standalone-×××.jar無異。同理我們可以用docker logs sleepy_bhabha查看node的啓動信息。

昨晚這一切我們,在瀏覽器中輸入http://192.168.99.100:5555/grid/console,有如下信息:

selenium結合docker構建分佈式測試環境

可以看出來,我們的hub中有個新註冊的裝有firefox瀏覽器且版本號爲50.0的node節點,我們本地寫個簡單的腳本來測試下這個簡單的grid佈局代碼如下:

#coding=utf-8
from  selenium import webdriver
firefox_capabilities ={
    "browserName": "firefox",
    "version": "50.0",#注意版本號一定要寫對
    "platform": "ANY",
    "javascriptEnabled": True,
    "marionette": True,
}
browser=webdriver.Remote("http://192.168.99.100:5555/wd/hub",desired_capabilities=firefox_capabilities)#注意端口號5555是我們上文中映射的宿主機端口號
browser.get("http://www.baidu.com")
browser.get_screenshot_as_file("D:/baidu.png")
browser.close()

代碼比較簡單,就是打開node端的firefox瀏覽器,輸入百度後截圖,一切看起來都是那麼順利,但是細心的同事可能發現至少有以前2個問題:

第一、我們對百度的截圖中文是亂碼的,這個不能忍啊!!!

第二、我們再次在IDE運行下這個腳本,發現一直處於阻塞狀態也不報錯,得不到hub遠程的執行信息~~

對於問題一,因爲我們要涉及到關於docker的鏡像製作等操作我們放在後面介紹,先來看看第二個問題,爲什麼我們再次運行這個腳本無法正常執行?

首先,這是和docker無關的,你在真實的PC機上也是同樣的結果!既然和docker無關那就是selenium-server-standalone-3.3.1.jar這個包的bug了?答案是:也不是!!那到底是什麼我們來看看是什麼原因。我們打開hub的log(docker logs selenium_hub)。截圖如下:

selenium結合docker構建分佈式測試環境

看出來,第一次我們得到了一個包含有Capabilties請求後成功的建立了一個session,這個對應於我們第一次在IDE運行上文的腳本;我們第二次再次運行這個腳本,雖然hub得到了這個請求,但是沒有成功的建立了一個新的session。那麼問題可能出在node端它不容許建立新的session,所以我們一直處於阻塞狀態(阻塞的時間有node的啓動參數newSessionWaitTimeout決定),除非我們的第一個session被人爲的或者由於超時被自動切斷後,才能重新建立!

問題知道了,我們如何改進呢?對的就是改變node的啓動參數。我們seleniu/node鏡像默認的關鍵參數如下:

  "capabilities": [
    {
      "maxInstances": $NODE_MAX_INSTANCES,#最大的瀏覽器實例
       ......
   
    }
  "maxSession\": $NODE_MAX_SESSION #最大的Session數目

 由於默認的maxSession爲5而maxInstances爲1,所以取最小的一個,node端只運行有一個session的存在,所以我們第二次運行處於阻塞狀態!!這些信息我們會在後面對其鏡像的構建文件Dockerfile講解時提到。作者爲什麼要將maxInstances設置爲1?我的理解是:一般用到Grid佈局的都是一些比較大型的測試體系,一般環境搭建好了測試腳本寫好了保證一天有一個測試結果就好。我們默認的timeout時間是30000S從上面我們hub的啓動日誌可以看出來,也就是說如果hub在30000s內沒有執行任何操作就會自動切斷!這個timeout的時間是相當長的關於這個timeout的時間長短我覺得與你的testcase有關,比如你在腳本中有個比較耗時的操作例如Sleep(100),睡眠100S再進行後續操作,如果你的timeout設置爲30s,那麼在30S的時候hub會主動切斷這個連接,也就是說你後面的用例是不會再執行了....這個就是個仁者見仁,智者見智的事情了,但是我覺得把timeout搞長點好點。想要多次調試腳本,大不了我們把maxInstances與maxSession設置數目大點就了。實在不行你也可以先docker stop+node 容器的ID,再docker start +node 容器ID就好了!

說了第二個問題的一些看法,我們重點來看看如何解決第一個問題,在解決第一個問題的同時,我們修改一些配置,一併把第二個問題給解決了!!

我們先來看看selenium/hub與seleniu/node-firefox的源Dockerfile文件,源碼的地址爲:https://github.com/SeleniumHQ/docker-selenium,你可以下載或clone下來。我們來看看這個項目的結構:

selenium結合docker構建分佈式測試環境

Base是selenium鏡像的基礎,裏面的Dockerfile除了在Ubuntu鏡像的基礎上安裝了一些工具包,主要工作如下:

1.還有搭建了一個java運行環境,用來運行後來的selenium-server-standalone-3.3.1.jar

2.下載了selenium-server-standalone-3.3.1.jar放在容器的/opt/selenium文件夾下

Hub文件夾裏面的Dockerfile就是用來build selenium/hub鏡像的,是基於上面的Base,主要工作如下:

1.定義了node啓動的一些參數,發現它把裏面的timeout參數改成30s了。我並沒有pull最新的selenium/hub,你們現在pull的可能timeout參數就是30s,我的是30000s。

2.定義了容器啓動的腳本entry_point.sh,並把他複製到容器的/opt/bin下

NodeBase是一切node鏡像的基礎,我們查看裏面的Dockerfile發現它是基於我們上面提到的Base的,主要工作如下:

1.定義了node容器的啓動腳本entry_point.sh並複製到容器的/opt/bin/下。

NodeFirefox是用來build我們pull下來的selenium/node-firefox它是基於我們上面提到的NodeBase。我們查看它的Dokcerfile文件,發現它的主要工作如下:

1.下載了版本號爲52.0.2的firefox瀏覽器,並做了一些配置。

2.下載了GeckoDriver,我們知道高版本的瀏覽器必須要通過GeckoDriver來啓動一個socket,這也是selenium3.0誕生的主要原因。2.0是無法啓動高版本的firefox的

3.設置fire-node的一些啓動參數,我們清楚的看到NODE_MAX_INSTANCES 1與ENV NODE_MAX_SESSION 1的配置,所以如何要改變我們問題二的情況,在重新構建鏡像的時候,就要重新定義這些環境變量了。

對上面的解釋做個說明:

1.Dockerfile是用來build鏡像的。指令爲docker build -t 鏡像名稱  Dockerfile路徑,例如:docker build -t selenium/my-firefxo-node . 用當前路徑下的Dockerfile文件來創建一個 selenium/my-firefxo-node鏡像文件。

2.Dockerfile一些基礎知識可以百度瞭解,無非就是一些shell指令集的大集合。

說完了這些,我們就來解決我們遇到的第一個和第二個問題,思路很簡單,對的,就是利用的selenium/firefox-node重新構建鏡像。

截圖中文亂碼的問題我的考慮如下:

1.docker的Ubuntu版本是沒有中文版的,所以我們要下載中文包,並把默認語言配置成中文

2.僅僅做了1的操作後,我們重新運行build的鏡像,發現還是亂碼,查閱資料明白可能是firefox必須支持相應的中文字體纔可以避免亂碼的情況,所以我們最終的Dockerfile內容如下:

FROM selenium/node-firefox
#改變node的啓動參數
ENV NODE_MAX_INSTANCES 10
ENV NODE_MAX_SESSION 10
# 配置中文
RUN sudo locale-gen zh_CN.UTF-8 &&\
 sudo DEBIAN_FRONTEND=noninteractive dpkg-reconfigure locales
RUN sudo locale-gen zh_CN.UTF-8  
ENV LANG zh_CN.UTF-8  
ENV LANGUAGE zh_CN:zh  
ENV LC_ALL zh_CN.UTF-8
#更新軟件包索引
RUN sudo apt-get update -qqy
#安裝基本字體
RUN sudo apt-get -qqy --no-install-recommends install \
    fonts-ipafont-gothic \
    xfonts-100dpi \
    xfonts-75dpi \
    xfonts-cyrillic \
    xfonts-scalable
#安裝文泉驛微米黑字體
RUN sudo apt-get -qqy install ttf-wqy-microhei \
  && sudo ln /etc/fonts/conf.d/65-wqy-microhei.conf /etc/fonts/conf.d/69-language-selector-zh-cn.conf

 說到這遇到一個現實的問題是,我們建立了上面的Dokcerfile文件,但是是在windows平臺上的,例如我是放在D盤的Sikilu這個文件夾下的,我們docker主機如何訪問這個Sikilu文件夾呢?

首先,我們要明白個道理,docker是不能運行在windows平臺,我們能在windows平臺使用是因爲它運行在Oracle VM VirtualBox裏面的!!所以在windows品臺搭建docker時給你安裝了個VirtualBox虛擬機。這就好辦了,我們打開這個虛擬機把D盤的Sikilu設置爲共享文件夾就好了。具體截圖如下:

selenium結合docker構建分佈式測試環境

設置完後,我們重啓虛擬機中名稱爲default這個docker主機。

我們在Xshell中切換到根目錄cd /發現了共享的文件夾:

selenium結合docker構建分佈式測試環境

我們把上面的Dockerfile放到我們共享的這個文件夾下並切換進去。執行docker build -t selenium/my_node-firefox .   

build的過程可能要點時間最終build成功!我們運行docker images

selenium結合docker構建分佈式測試環境

可以看到我們build的鏡像。我們先停掉上面用selenium/node-firefox啓動的容器:

selenium結合docker構建分佈式測試環境

我們運行如下指令:docker run -P -d --link selenium_hub:hub  selenium/my_node-firefox

selenium結合docker構建分佈式測試環境

由於selenium/my_node-firefox是基於selenium/node-firefox的,所以容器內部也是個版本爲50.0的firefox瀏覽器。但是我們爲該容器下載了中文包且下載了相應的字體,改變了firefox瀏覽器中文亂碼的問題,同時改變了firefox node的啓動參數,使它能夠同時建立10個session!具體數字多少自己更改,當然也可以改其他的啓動參數!

我們運行上面的測試腳本,發現問題能得到有效的解決,當然運行超過10次就又阻塞了,原因上面說的很清楚了我們的最大session設置了10!

最後一個問題,我們如何建立其他版本號的node-firefox。上面說了我們pull下來的是50.0的,比如我要構建集羣測試每臺機器上不同版本的firefox,那麼我們就要創建不同版本的鏡像!

這邊有2中思路,第一種思路想法也簡單

1.下載我們特定版本的瀏覽器到我們共享的Sikilu文件夾中。

2.運行我們上面build的selenium/my_node-firefox這個鏡像把這個共享的文件夾掛載進去。(-v參數)

docker run -P  -v /Sikilu/:/Sikilu --link selenium_hub:hub selenium/my_node-firefox

3.我們刪除該容器/opt下的firefox-50.0這個瀏覽器,同時解壓掛載的宿主機Sikilu中的特定版本的firefox到/opt目錄

4.運行rm -rf /usr/bin/firefox

5.ln -fs /opt/firefox-$FIREFOX_VERSION/firefox /usr/bin/firefox

6.exit這個容器運行docker commit -m "firefox-47.0.2"  當前容器的ID  鏡像名稱,例如docker commit -m "firefox-47.0.2"  2d96630b4f07(我們進入的容器id號)  selenium/node-firefox47.0.2(新的鏡像名稱)

上面的一系列操作,我們得到了一個特定版本的鏡像,下次啓動我們run這個鏡像就可以了。

第二種思路,在我們新build的selenium/my_node-firefox這個基礎新建Dockerfile,重新構建鏡像(建議這種方式畢竟高端點...)內容如下:

FROM selenium/my_node-firefox
#指定新構建firefox版本
ARG FIREFOX_VERSION=47.0.2
#刪除已存在firefox瀏覽器
RUN a=$(find /opt/  -name "firefox" |cut -d/ -f 3)&&sudo rm -rf /opt/$a&&sudo rm -rf /usr/bin/firefox
#下載指定版本的瀏覽器
RUN sudo apt-get update -qqy \
&& sudo rm -rf /var/lib/apt/lists/* /var/cache/apt/* \
&& sudo wget --no-verbose -O /tmp/firefox.tar.bz2 https://download-installer.cdn.mozilla.net/pub/firefox/releases/$FIREFOX_VERSION/linux-x86_64/en-US/firefox-$FIREFOX_VERSION.tar.bz2 \
&& sudo tar -C /opt -xjf /tmp/firefox.tar.bz2 \
&& sudo rm /tmp/firefox.tar.bz2 \
&& sudo mv /opt/firefox /opt/firefox-$FIREFOX_VERSION \
&& sudo ln -fs /opt/firefox-$FIREFOX_VERSION/firefox /usr/bin/firefox

 同樣,我們切換到共享的Sikilu文件夾中運行:docker build -t selenium/node-firefox47.0.2  .也可以建立一個firefox版本爲47.0.2的鏡像,當我們run這個鏡像來啓動容器時,註冊進hub的就是這個版本的瀏覽器!!

最後一個問題收尾,就是我們最開始提到的run一個鏡像時的--link的參數。

docker通過link將source container中定義的環境變量全部導入到received container中。我們先來看看上面提到NodeBase文件下的entry_point.sh這個腳本,我們截取最關鍵的一部分:就是執行selenium-server-standalone.jar這個jar包的一段:

xvfb-run -n $SERVERNUM --server-args="-screen 0 $GEOMETRY -ac +extension RANDR" \
  java ${JAVA_OPTS} -jar /opt/selenium/selenium-server-standalone.jar \
    -role node \
    -hub http://$HUB_PORT_4444_TCP_ADDR:$HUB_PORT_4444_TCP_PORT/grid/register \
    -nodeConfig /opt/selenium/config.json \
    ${SE_OPTS} &
NODE_PID=$!

 上面的$HUB_PORT_4444_TCP_PORT表示我們hub的端口號,$HUB_PORT_4444_TCP_ADDR這個代表hub的IP地址,這些是怎麼來的?其實這個是因爲我們使用link參數,docker自動生成這些參數。我們進入到由鏡像selenium/my_node-firefox啓動的容器(sudo docker exec -it 24c5fb9a649a  /bin/bash),輸入env

selenium結合docker構建分佈式測試環境

發現了這2個字段,其實這個容器的環境變量被分成5個部分,一個部分就是ALIASDB_PORT開頭的一系列變量,這些變量會有很組,每組變量的命名格式如下:

selenium結合docker構建分佈式測試環境

其中<alias>是我們source container的別名;<port>是在Dockerfile中使用EXPOSE導出的端口,還有docker run 的時候使用-p導出的端口;<protocol>則是這些端口對應的協議。

現在明白上文中爲什麼--link selenium/hub冒號後面爲什麼要跟hub或者HUB了吧,只有寫成這樣纔會找到HUB_PORT_4444_TCP_PORT與$HUB_PORT_4444_TCP_ADDR這2個變量,免得容器是啓動不起來的!!

到這裏結束吧,本來還想多寫點關於docker的用法知識。但是打字打的想吐了,可能本文中一些東西說的不清不楚,但是我相信您通過查閱資料一定能得到相應的解決!!

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