《Selenium自动化测试实战:基于Python》之 基于Docker与Selenium Grid的测试技术
结合Bing搜索案例在Chrome和Firefox浏览器中实现兼容性测试。在经过对Selenium、Docker和Selenium Grid相关知识的学习后,你想到了什么?是不是通过使用Docker + Selenium Grid就能够完成基于不同浏览器的兼容性测试呢?是的,这确实是个好主意。
基于Docker与Selenium Grid的测试技术10.1 Selenium Grid简介
尽管即将推出的Selenium 4.0对Selenium Grid的一些新特性进行了说明(截至本书完稿时,Selenium 4.0尚未正式发布),但是从目前看,官方并没有太多详细文档供大家参考,所以本书仍结合目前广泛使用的Selenium Grid 版本进行讲解。参见官网上的描述,Selenium Grid是智能代理服务器,允许Selenium将测试命令路由到远程Web浏览器实例,目的是提供一种在多台计算机上并行运行测试的简便方法。使用Selenium Grid,一台服务器可以充当将JSON格式的测试命令路由到一个或多个已注册Grid节点的中枢,以获得对远程浏览器实例的访问。Selenium Grid允许我们在多台计算机上并行运行测试,并集中管理不同的浏览器版本和浏览器配置。如图10-1所示,可以看到Selenium Grid主要由Hub和Node两部分构成。可以使用Python、Java、C#等语言编写和测试Selenium脚本,每个Selenium Grid仅有一个Hub,客户端脚本可以指定连接到这个Hub(主控节点或者叫集线器),Hub接收客户端脚本的运行测试请求,同时将这些测试请求分发到已注册的一个或多个节点以执行并收集运行结果。Selenium Grid中可以有一个或多个Node(节点)。作为节点的机器不必与Hub或其他Node具有相同的操作系统或浏览器。换言之,某个Node可能使用的是Windows操作系统,而在Windows操作系统中安装的是Internet Explorer浏览器,另外的Node可能使用的是Linux操作系统、macOS,而它们安装的浏览器可能是Firefox、Safari、Chrome等。这些Node的设置结合测试来讲,就是看想做哪些操作系统和浏览器版本的兼容性测试,在实际工作中请结合测试执行计划和策略进行选择。
图10-1 Selenium Grid的组件构成
10.2 基于Docker的Selenium Grid的相关配置
Docker Hub提供了Selenium Grid的相关镜像文件以供使用,如图10-2所示。
图10-2 Selenium Grid的相关镜像文件
这里,我们使用docker pull命令分别将这3个镜像文件拉取下来,对应的拉取命令如下。
docker pull selenium/hub docker pull selenium/node-chrome docker pull selenium/node-firefox
拉取镜像文件到本地后,可以使用docker images命令查看一下相关镜像的信息,如图10-3所示。
图10-3 Selenium Grid的相关镜像信息
这里我们先测试一下Hub与Node之间的连通性。
创建并启动Hub容器,如图10-4所示。
图10-4 创建并启动Hub容器
创建并启动 chromenode容器节点,如图10-5所示。
图10-5 创建并启动chromenode容器节点
创建并启动 firefoxnode容器节点,如图10-6所示。
图10-6 创建并启动firefoxnode容器节点
接下来,在本机浏览器的地址栏中输入http://localhost:4444/grid/console并按Enter键,打开Selenium Grid的控制台,出现图10-7所示页面。
图10-7 Selenium Grid的控制台
从图10-7可知,当前使用的Selenium Grid 版本为3.141.59,连接到Hub的两个Node中,IP地址为172.17.0.4的Linux操作系统使用的是75.0版本的Firefox浏览器,IP地址为172.17.0.3的Linux操作系统使用的是81.0.4044.92版本的Chrome浏览器。默认情况下,Hub使用的是4444端口,而Node在本例中使用的是5555端口。如果在同一个容器中出现端口冲突等情况,则需要根据实际情况进行调整以避免端口冲突情况再次发生。
10.3 基于Docker + Selenium Grid的案例演示
下面结合Bing搜索案例在Chrome和Firefox浏览器中实现兼容性测试。在经过对Selenium、Docker和Selenium Grid相关知识的学习后,你想到了什么?是不是通过使用Docker + SeleniumGrid就能够完成基于不同浏览器的兼容性测试呢?是的,这确实是个好主意。
但是,为了让Selenium测试脚本在不同的浏览器中运行,又需要做些什么呢?
在脚本设计上,需要做一些改变。通常情况下,要在脚本运行时指定主机和端口号,使用的脚本如下。
import time from selenium import webdriver from selenium.webdriver.common.desired_capabilities import DesiredCapabilities driver = webdriver.Remote( command_executor='http://192.168.1.102:4444/wd/hub', desired_capabilities=DesiredCapabilities.CHROME) base_url = 'https://cn.bing.com' driver.get(base_url) driver.save_screenshot('chrome.png') driver.close()
通常在执行时,只需要指定Hub的地址(http://192.168.1.102:4444/wd/hub)。这里宿主机的IP地址信息如图10-8所示,Hub会将脚本自动分配给Node去执行。
图10-8 宿主机的IP地址信息
- command_executor:选填参数,可指定远程服务器的URL字符串或自定义远程连接,默认为http://127.0.0.1:4444/wd/hub。
- desired_capabilities参数:必填参数,可根据情况配置为在启动浏览器会话时请求功能字典。这里我们使用的是DesiredCapabilities.CHROME,对应的源代码如下所示。
class DesiredCapabilities(object): """ Set of default supported desired capabilities. Use this as a starting point for creating a desired capabilities object for requesting remote webdrivers for connecting to selenium server or selenium grid. Usage Example:: from selenium import webdriver selenium_grid_url = "http://198.0.0.1:4444/wd/hub" capabilities = DesiredCapabilities.FIREFOX.copy() capabilities['platform'] = "WINDOWS" capabilities['version'] = "10" driver = webdriver.Remote(desired_capabilities=capabilities, command_executor=selenium_grid_url) Note: Always use '.copy()' on the DesiredCapabilities object to avoid the side effects of altering the Global class instance. """ FIREFOX = { "browserName": "firefox", "acceptInsecureCerts": True, } INTERNETEXPLORER = { "browserName": "internet explorer", "version": "", "platform": "WINDOWS", } EDGE = { "browserName": "MicrosoftEdge", "version": "", "platform": "ANY" } CHROME = { "browserName": "chrome", "version": "", "platform": "ANY", } OPERA = { "browserName": "opera", "version": "", "platform": "ANY", } SAFARI = { "browserName": "safari", "version": "", "platform": "MAC", } HTMLUNIT = { "browserName": "htmlunit", "version": "", "platform": "ANY", } HTMLUNITWITHJS = { "browserName": "htmlunit", "version": "firefox", "platform": "ANY", "javascriptEnabled": True, } IPHONE = { "browserName": "iPhone", "version": "", "platform": "MAC", } IPAD = { "browserName": "iPad", "version": "", "platform": "MAC", } ANDROID = { "browserName": "android", "version": "", "platform": "ANDROID", } PHANTOMJS = { "browserName": "phantomjs", "version": "", "platform": "ANY", "javascriptEnabled": True, } WEBKITGTK = { "browserName": "MiniBrowser", "version": "", "platform": "ANY", } WPEWEBKIT = { "browserName": "MiniBrowser", "version": "", "platform": "ANY", }
从DesiredCapabilities类的源码可知DesiredCapabilities.CHROME是DesiredCapabilities类定义的字典对象。
这里采用多线程的方式,分别在Chrome和Firefox浏览器中执行Bing搜索业务。
Grid_Test.py文件的内容如下。
from threading import Thread from selenium import webdriver from time import sleep,ctime from selenium.webdriver.common.by import By def Test_Bing(Host, Browser): caps = {'browserName': Browser} driver = webdriver.Remote(command_executor=Host, desired_capabilities=caps) driver.get('http://www.bing.com') driver.find_element(By.ID,'sb_form_q').send_keys('异步社区') driver.find_element(By.ID,'sb_form_go').click() PicName=Browser+'_result'+'.png' driver.save_screenshot(PicName) assert ('没有与此相关的结果' not in driver.page_source) sleep(2) driver.close() if __name__ == '__main__': pcs = {'http://192.168.1.102:4444/wd/hub': 'chrome', 'http://localhost:4444/wd/hub': 'firefox' } threads = [] tds=range(len(pcs)) #创建线程 for host, browser in pcs.items(): t = Thread(target=Test_Bing, args=(host, browser)) threads.append(t) #启动线程 for i in tds: threads[i].start() for i in tds: threads[i].join()
从上面的脚本可以看到,这里创建了一个名为Test_Bing()的函数,它包含两个参数,分别用来指定主机和浏览器。这个函数的执行意图就是根据远程服务器的URL字符串和传入的浏览器名称字符串,在对应的浏览器中执行搜索业务,且搜索词为“异步社区”。然后对执行结果进行截图,截图的名称为对应浏览器的名称加上_result.png,最后对搜索结果进行断言。需要说明的是,这里进行截图的目的不仅是看一下结果,还要看一下执行过程。在使用Selenium Grid时,由于测试过程中不会出现浏览器,因此看不到执行过程。如果还想看看不同的容器在执行过程中的界面,那么可以使用VNC Viewer连接到对应的容器(但需要下载对应的selenium/node-firefox-debug和selenium/node-chrome- debug镜像文件,以debug结尾的镜像都带有VNC服务器,在本机上安装VNC客户端后即可远程连接。5900端口为VNC Viewer的监听端口,因此做了端口映射),如图10-9和图10-10所示。
图10-9 创建并启动Debug版本的节点容器
图10-10 使用VNC Viewer观察节点容器的脚本执行情况
事实上,这对于测试工作并没有太多意义,因而不做太多文字赘述。
主函数定义了一个包含两个元素的字典,这里虽然使用了同一个地址,但采用的是两种不同的表示方式(宿主机的IP地址为192.168.1.102),而localhost也表示宿主机。那么为什么不都用192.168.1.102或localhost呢?这是因为字典的键(key)是不允许重复的。接下来,我们创建了一个线程列表,以pcs字典的键、值作为Test_Bing()函数的参数添加到这个线程列表中,而后启动这个线程列表中的各个线程。
在运行脚本前,需要保证创建并启动Hub和Node容器(这里应用的是非Debug版本的Node镜像),如图10-11所示。
图10-11 创建并启动Hub和Node容器
脚本执行完毕后,将会生成chrome_result.png和firefox_result.png两个图片文件,如图10-12所示。
图10-12 脚本执行完毕后生成的图片文件
在本次兼容性测试中,这两个浏览器执行了相同的Bing搜索业务,它们的页面展示、布局、内容基本是相同的,但存在两个小的问题。第一个小问题就是在Chrome浏览器中搜索到的结果有855 000条(见图10-13),而在Firefox浏览器中搜索到的结果有859 000条(见图10-14),它们是不一致的。另一个小问题是,Firefox浏览器会显示Sign in和登录图标,而Chrome浏览器没有。从理论上讲,这是两个严重度级别较低的小Bug,建议针对这两个小的差异,与产品及研发人员再确认一下,产品、测试及研发人员应统一、明确需求,明确后再修改需求或代码,使两者保持一致。
图10-13 在Chrome浏览器中搜索到的结果
图10-14 在Firefox浏览器中搜索到的结果