从左至右,任一条路都是通的。Selenium webdriver支持多种编程语言,并支持主流浏览器。你可以使用任意语言对不同浏览器进行测试。在这里我选择的是python语言。
为什么是python
1. C#对于我来说太容易
2. java也很熟
3. 我想挑战一门新语言
好了不吹牛了。之前看过一些python的基础教程,python对语法的精简性,以及对代码格式的规范等方面还是稍微打动了一下我。有关python的基础教程请参看:http://www.runoob.com/python/python-tutorial.html
开发环境,我使用的是vscode,在扩展搜索python,找到微软官方的python插件安装一下即可,安装后要重启vscode。
操作系统也是要安装python的,这里我安装的是python2.7,因为我还有其他工作依赖这个版本,所以没有使用3.0以上。
接下来是selenium的安装,直接执行python的包管理命令pip install selenium即可。默认会安装最新版本,这里我安装的是selenium 3.14.0
最后就是面向不同浏览器的驱动了,一般都是各自浏览器厂商提供,这里给出主流的几个地址:
chrome: https://sites.google.com/a/chromium.org/chromedriver/downloads
Firefox: https://github.com/mozilla/geckodriver/releases
环境差不多了,接下来开始正式写代码。
defsetUpClass(cls):
options = Options()
options.add_argument("--no-sandbox")
options.headless =True
options.add_argument('--ignore-certificate-errors')
options.add_argument('--ignore-ssl-errors')
driver = webdriver.Chrome(executable_path="D:\\python_test\\chromedriver.exe",service_log_path="D:\\python_test\\chrome.log",chrome_options=options)
print("Chrome headless mode has been launched.")
如上,这里以chrome为例,通过Options来指定一些参数,注意headless属性的设置,我希望整个测试期间不要看到浏览器,这就是所谓“无头”的概念。我们要通过指定chrome驱动文件chromedriver.exe的地址,来实例webdriver对象,如果有需要可以设置service_log_path来输出日志。
下面我们以chrome为例,来对我们的一个系统做一个测试。
一般网站访问都是需要登录的,不过无非就是输入个用户名和密码,然后点击登录嘛?下面我们来模拟这个过程:
defloginAndChooseHotel(cls):
# 登录
cls.driver.get("https://.../Login")
# 输入用户名密码,登录,跳转到酒店选择页
通过get方法访问登录地址,在加载完毕后通过save_screenshot方法对结果截图。
页面的用户名密码的输入框都是设置过id属性的,于是这里可以简单的通过find_element_by_id方法找到对应输入框,并写入对应的账号密码。最后,仍然是通过id找到登录按钮并点击它,跳转到新的页面并再次截图。
如图,接下来就是酒店的选择,在这里选择我最爱的竹子林客栈:
hotel =self.driver.find_element_by_id("listView").find_element_by_xpath("//p[contains(text(),'CIZZLS')]")
hotel.click()
这里有些复杂,酒店的选择页面是一系列复杂html标记的组合,如下:
...
...
[CIZZLS] CITY INN BEIJING
...
...
可以看出,在listView下有多个复杂div来分别表示不同的酒店,这些div是后台动态生成的,所以也无法设置id属性。解决方案就是find_element_by_xpath方法了
hotel =self.driver.find_element_by_id("listView").find_element_by_xpath("//p[contains(text(),'CIZZLS')]")
hotel.click()
find_element_by_xpath支持通过xpath表达式的方式来定位节点,这里通过定位文本为“CIZZLS”的节点,并单击来实现选择,然后跳转到首页,最后照例拍照留念。
接下来我们需要选择左侧菜单的“酒店”,进入酒店管理页面,这些菜单也没有设置id属性,但都是链接,所以依然通过xpath表达式解决:
hotelLink =self.driver.find_element_by_xpath("//a[@href='/.../.../List']")
hotelLink.click()
执行后将跳转到酒店列表页面,如图:
这里需要说明的是,这个表格的加载是采用ajax异步的方式,包括点击查询按钮也都是ajax异步刷新表格的方案。这会产生一些问题。首先我们要明确webdriver一次页面跳转结束的标志,当后台有response响应,webdriver就认为页面请求结束,也就认为页面彻底被加载,但由于ajax请求是异步的,webdriver首先无法知道该页面是否有异步请求,其次也无法知道ajax的请求何时才返回。所以这里如果还是沿用之前的方案,那么拍照的结果很可能是表格尚未加载到数据的状态。解决方案也是有的,比如你可以sleep若干秒,当然还有更合理的方案,代码如下:
WebDriverWait(self.driver,10).until_not(EC.visibility_of(processDiv),"no list error")
这里采用了WebDriverWait组件,它的作用是在达到超时秒数前有条件的等待,当条件满足则不再等待。这里,表格开始加载时会弹出一个提示窗口,当加载完毕后窗口也会消失。所以我的方案是如果等待窗口显示我就堵塞,如果窗口消失则退出等待。通过Visibility_of方法判断等待窗口是否显示,再通过until_not达到取反的目的。
刚才说到查询,酒店列表页面是支持条件查询的,我们来测试一下:
WebDriverWait(self.driver,10).until_not(EC.visibility_of(processDiv),"no list error")
这里需要说一下的是send_keys方法以及中文问题。send_keys支持模拟键盘批量录入数据,但对于中文,需要加入u前缀,表示这是unicode字符,否则分分钟报错给你看,截图如下:
既然记录查到了,下面就要编辑查看该条记录的详情。这个表格的实现其实也很复杂,依然祭出xpath:
tr =self.driver.find_element_by_xpath("//tr[td[text()='CIZZLS']]")
editLink = tr.find_element_by_xpath("//td[last()]/a")
editLink.click()
思路其实很简单,xpath到包含CIZZLS的行,然后二次xpath到最后一个单元格,click跳转到酒店编辑页。
可以看到录入项还是很多的,不过大多都是有id属性的,找起来方便的很。这里我们挑选一个下拉框来做一个修改的例子。我们来修改一下品牌吧?
hotelGroupSelect =self.driver.find_element_by_xpath("//span[select[@id='HotelGroupCode']]")
hotelGroupSelect.click()
time.sleep(1)
self.driver.find_element_by_id("HotelGroupCode_listbox").find_element_by_xpath(u"//li[text()='品牌B']").click()
selectedText = hotelGroupSelect.find_element_by_xpath("span/span[@class='k-input']").text
self.assertEqual(selectedText,u"品牌B")
print("test_hotel success.")
一般的select下拉框,我们可以直接修改它的value或设置selected属性即可,但这里的下拉框用到了第三方kendoui框架,是一系列html组合出来的东西。所以这里采用模拟行为的方式,即实际情况是需要点击一下下拉框,再从弹出的列表中找到需要的item再点击一次。另外还需要注意几个地方。首先下拉框是动态弹出的,实际发现在弹出之前就点击下拉项可能造成失败,也许这里控件会有一些js处理,如绑定事件什么的?这里就不得而知了,索性sleep一下吧?注意这里有用到assertEqual断言,这是python的unintest框架的一部分,来确定品牌下拉框是否确实被修改了。通过断言,我们能够确实的知道测试的准确性,不然只靠图片一张张看起来也挺累的。
关于测试报告,网上一堆关于HtmlTestRunner的介绍,但这里并不推荐,一方面这个库已经很久没更新了,另一方面,html格式也不便于传输。个人还是建议excel格式,在这里推荐xlswriterhttps://xlsxwriter.readthedocs.io/ ,对excel的操作很自由,精确到单元格,也可以将测试截图设置到单元格,当然对编码能力的要求也较高。
最后再说一下unittest。通过unittest可以写一系列的测试用例,也可以决定在单个测试用例执行前后做什么?或在测试的开始前后做什么?包括测试用例之间如果有执行的先后次序也是可以设置的。
最后贴上完整代码:
#!/usr/bin/python
# coding=utf-8
importunittest
importtime
fromseleniumimportwebdriver
classTest(unittest.TestCase):
@classmethod
defsetUpClass(cls):
cls.browserName ="chrome"
ifcls.browserName =="firefox":
# https://github.com/mozilla/geckodriver/releases
options = webdriver.FirefoxOptions()
options.headless =True
driver = webdriver.Firefox(executable_path="D:\\python_test\\geckodriver.exe",service_log_path="D:\\python_test\\firefox.log",options=options)
print("Firefox headless mode has been launched.")
elifcls.browserName =="ie":
# options = webdriver.IeOptions()
# options.headless = True
# driver = webdriver.Ie(executable_path="D:\\python_test\\IEDriverServer.exe",service_log_path="D:\\python_test\\ie.log",options=options)
print("IE headless mode has been launched.")
else:
# https://sites.google.com/a/chromium.org/chromedriver/downloads
options = Options()
options.add_argument("--no-sandbox")
options.headless =True
options.add_argument('--ignore-certificate-errors')
options.add_argument('--ignore-ssl-errors')
driver = webdriver.Chrome(executable_path="D:\\python_test\\chromedriver.exe",service_log_path="D:\\python_test\\chrome.log",chrome_options=options)
print("Chrome headless mode has been launched.")
cls.driver = driver
cls.loginAndChooseHotel()
@classmethod
deftearDownClass(cls):
@classmethod
defloginAndChooseHotel(cls):
# 登录
cls.driver.get("https://.../Login")
# 输入用户名密码,登录,跳转到酒店选择页
deftest_hotel(self):
# 选择竹子林酒店,跳转到home页
hotel =self.driver.find_element_by_id("listView").find_element_by_xpath("//p[contains(text(),'CIZZLS')]")
hotel.click()
# 点击酒店链接,跳转到酒店列表,需等待列表加载
hotelLink =self.driver.find_element_by_xpath("//a[@href='/.../.../List']")
hotelLink.click()
WebDriverWait(self.driver,10).until_not(EC.visibility_of(processDiv),"no list error")
# 输入条件查询,仍需等待列表加载
WebDriverWait(self.driver,10).until_not(EC.visibility_of(processDiv),"no list error")
# 在查询结果列表中找到编辑按钮,点击跳转到酒店编辑页面
tr =self.driver.find_element_by_xpath("//tr[td[text()='CIZZLS']]")
editLink = tr.find_element_by_xpath("//td[last()]/a")
editLink.click()
# 下拉框的处理,需要等待1秒,否则可能出现列表尚未加载造成选择item不能的情况,最后可以做一个断言,确定item被选中
hotelGroupSelect =self.driver.find_element_by_xpath("//span[select[@id='HotelGroupCode']]")
hotelGroupSelect.click()
time.sleep(1)
self.driver.find_element_by_id("HotelGroupCode_listbox").find_element_by_xpath(u"//li[text()='品牌B']").click()
selectedText = hotelGroupSelect.find_element_by_xpath("span/span[@class='k-input']").text
self.assertEqual(selectedText,u"品牌B")
print("test_hotel success.")
deftest_roomtype_setting_wizard(self):
# todo
print("test_roomtype_setting_wizard success.")
if__name__ =="__main__":
unittest.main()
领取专属 10元无门槛券
私享最新 技术干货