UI自动化测试

从左至右,任一条路都是通的。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()

  • 发表于:
  • 原文链接:https://kuaibao.qq.com/s/20180908G0D5II00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

同媒体快讯

扫码关注云+社区

领取腾讯云代金券