![de2ee1de714605e065a0560863f392be.png](https://i-blog.csdnimg.cn/blog_migrate/f80605c59a55af0ee45a682aa888bed1.jpeg)
Webdriver运行原理 转帖请注明出处!谢谢
在开发Webdriver的自动化脚本过程中,Webdriver后台在创建WebDriver实例的过程中,先确认浏览器的原生组件中是否存在可匹配的版本。然后在目标浏览器里启动一整套Web Service(实际是浏览器厂商提供的driver, 比如IEDriver, ChromeDriver,它们都实现了WebDriver's wire protocol.),这套Web Service使用了Webdirver定义的通讯协议,名字叫做The WebDriver Wire Protocol。这套协议中定义了操作浏览器的执行动作包括打开、关闭、最大化、最小化、设置浏览器窗体大小、元素定位、元素点击、上传文件等。
WebDriver Wire协议是通用的。不管IE还是FirefoxDriver,ChromeDriver,甚至包括了AndroidDriver和iOS WebDriver,还有其他支持的三方浏览器,启动绑定在特定端口上的的Web Service。如FirefoxDriver初始化成功之后,默认会从http://localhost:7055开始,而ChromeDriver则大概是http://localhost:46350之类的。接下来,调用WebDriver的任何API,都需要执行ComandExecutor发送指令,这条指令实际上是HTTP request发送给监听端口上的Web Service。在HTTP request的body中,有符合WebDriver Wire协议规范的JSON格式的字符串来通知Webserver要求浏览器做什么。
可以这样理解:客户端脚本(java, python, ruby,c#)不能直接与浏览器通信,但可以用WebService作为通讯翻译器,WebService把客户端代码翻译成浏览器可以识别的代码(如js)。客户端(也就是测试脚本)创建1个session,在该session中通过http请求向WebService发送restful的请求,WebService翻译成浏览器懂得脚本传给浏览器,浏览器把执行的结果返回给WebService,WebService把返回的结果做了一些封装(一般都是json格式),然后返回给client,根据返回值就能判断对浏览器的操作是不是执行成功。
在登陆discuz代码中:
driver=webdriver.Firefox()#创建webdriver的firefox实例
driver.get(url) #打开bbs项目首页地址:r'http://192.168.0.110/discuz/forum.php'
在执行driver.get(url) 代码时,client也就是测试代码向Web Service(remote server)发送了如下的请求:POST session/122b12e4-2c9s-5y74-90e1-9sdfs972ldss/url,post_data {"url":" http://192.168.0.110/discuz/forum.php "} 。
通过post的方式请求localhost:port/hub/session/session_id/url地址,请求浏览器完成跳转url的操作。如果上述请求是可接受的,或者说Web Service是实现了这个接口,那么Web Service会跳转到该post data包含的url,并返回如下的response:{"name":"get","sessionId":"122b12e4-2c9s-5y74-90e1-9sdfs972ldss","status":0,"value":""}。该response中包含信息:name:Web Service端的实现的方法的名称,这里是get,表示跳转到指定url;sessionId:当前session的id;status:请求执行的状态码,非0表示未正确执行,这里是0,表示一切ok不必担心;value:请求的返回值,这里返回值为空,如果client调用title接口,则该值应该是当前页面的title。
如果client发送的请求是find_element_by_id('ls_username') 函数定位页面元素,则response的返回值是:{"name":"findElement","sessionId":"285b12e4-2b8a-4fe6-90e1-c35cba245956","status":0,"value":{"ELEMENT":"{2192893e-f260-44c4-bdf6-7aad3c919739}"}}。 name,sessionId,status跟前面例子一样,区别是该请求的返回值是ELEMENT:{2192893e-f260-44c4-bdf6-7aad3c919739},表示定位到元素的id,通过该id,client可以发送如click之类的请求与server端进行交互。
总结下来核心就是Webdriver是按照server–client的架构设计,server端就是remote server,可以是任意的浏览器:脚本启动浏览器后,该浏览器就是remote server,它的职责就是等待client发送请求并做出相应; client端简单说来就是我们的测试代码:测试代码表示的是执行的动作行为,比如启动浏览器,跳转到指定的url等,这些操作本质都是以http请求的方式发送给被server端(也就是被测浏览器),server接受请求,并执行相应操作,并在response中返回执行状态、返回值等信息。
Webdriver基本操作
WebDriver是一个接口,它展现了一个web浏览器,其自身主要实现了三个功能:控制浏览器本身 ,查找和选择元素 ,调试程序比如异常处理。
实例:
from selenium import webdriver
wb=webdriver.Firefox()
print('driver attributes:')
print(dir(wb))
运行结果:
driver attributes:
['CONTEXT_CHROME', 'CONTEXT_CONTENT', 'NATIVE_EVENTS_ALLOWED', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_file_detector', '_is_remote', '_mobile', '_switch_to', '_unwrap_value', '_web_element_cls', '_wrap_value', 'add_cookie', 'application_cache', 'back', 'binary', 'capabilities', 'close', 'command_executor', 'context', 'create_web_element', 'current_url', 'current_window_handle', 'delete_all_cookies', 'delete_cookie', 'desired_capabilities', 'error_handler', 'execute', 'execute_async_script', 'execute_script', 'file_detector', 'file_detector_context', 'find_element', 'find_element_by_class_name', 'find_element_by_css_selector', 'find_element_by_id', 'find_element_by_link_text', 'find_element_by_name', 'find_element_by_partial_link_text', 'find_element_by_tag_name', 'find_element_by_xpath', 'find_elements', 'find_elements_by_class_name', 'find_elements_by_css_selector', 'find_elements_by_id', 'find_elements_by_link_text', 'find_elements_by_name', 'find_elements_by_partial_link_text', 'find_elements_by_tag_name', 'find_elements_by_xpath', 'firefox_profile', 'forward', 'get', 'get_cookie', 'get_cookies', 'get_log', 'get_screenshot_as_base64', 'get_screenshot_as_file', 'get_screenshot_as_png', 'get_window_position', 'get_window_rect', 'get_window_size', 'implicitly_wait', 'install_addon', 'log_types', 'maximize_window', 'mobile', 'name', 'orientation', 'page_source', 'profile', 'quit', 'refresh', 'save_screenshot', 'service', 'session_id', 'set_context', 'set_page_load_timeout', 'set_script_timeout', 'set_window_position', 'set_window_rect', 'set_window_size', 'start_client', 'start_session', 'stop_client', 'switch_to', 'switch_to_active_element', 'switch_to_alert', 'switch_to_default_content', 'switch_to_frame', 'switch_to_window', 'title', 'uninstall_addon', 'w3c', 'window_handles']
技术解释:wb=webdriver.Firefox(),wb指初始化了一个firefox的实例对象,就是类似一个真实浏览器。使用内建的dir函数来获wb对象的所有属性和方法。
- Webdriver的窗体属性
- driver.current_url:用于获得当前页面的URL
实例:
from selenium import webdriver
wb=webdriver.Firefox()
wb.get('http://www.sohu.com')
print('当前URL:'+wb.current_url)
wb.get('http://www.youku.com')
print('当前URL:'+wb.current_url)
- driver.title:用于获取当前页面的标题
实例:
from selenium import webdriver
wb=webdriver.Firefox()
wb.get('http://www.sohu.com')
print('当前Ttile:'+wb.title)
wb.get('http://www.youku.com')
print('当前Title:'+wb.title)
- driver.page_source:用于获取页面html源代码
实例:
from selenium import webdriver
wb=webdriver.Firefox()
wb.get('http://www.sohu.com')
print(wb.page_source)
- driver.current_window_handle:用于获取当前窗口句柄
实例:
from selenium import webdriver
wb = webdriver.Firefox()
wb.get("http://www.sohu.com/")
print(wb.current_window_handle)
- driver.window_handles:用于获取所有窗口句柄
实例:
from selenium import webdriver
wb = webdriver.Firefox()
wb.get("http://www.sohu.com/")
newwindow = 'window.open("http://www.youku.com");'
wb.execute_script(newwindow)
handles = wb.window_handles
print('打开所有tab页的窗体句柄:')
print(wb.window_handles)
运行结果:
打开所有tab页的窗体句柄:
['13', '148']
技术解释:整段代码实现的是打开Firefox浏览器,开启两个tab页,分别打开搜狐和优酷两个网页。代码newwindow = 'window.open("http://www.youku.com");',newwindow字符串中保存的是一段JavaScript脚本,调用的是JS的内置对象window,Window 对象是 JavaScript 层级中的顶层对象,Window 对象代表一个浏览器窗口,open方法的作用是打开一个窗体,传入参数是在窗体中打开链接。wb.execute_script作用是执行一段JS脚本。
- Webdriver控制窗体常用方法
- driver.get(url):浏览器加载URL。
实例:
from selenium import webdriver
wb = webdriver.Firefox()
wb.get("http://www.sohu.com/")
技术解释:学习过程中会发现一个问wb.get执行时间很长,driver.get(url)实现的功能是跳转到指定的url,一直会等到页面加载完毕后才会执行完,重点在页面加载完成后,实际项目中如新闻类网站,视频类网站,电商网站等,一般页面前端代码做了很多效果或者实现了很多功能,造成加载资源过多,导致页面渲染时间过长。解决dirver.get不会因为加载时间引起的执行时间过长的问题,可以通过在driver中设置pageloadtimeout的方法设置加载页面时间限制,以及异常处理解决,代码如下:
import time
from selenium import webdriver
from selenium.common.exceptions import TimeoutException
startTime = time.time()
print("start time is: %0.3f"%startTime)
driver = webdriver.Firefox()
driver.set_page_load_timeout(3) # 设定页面加载限制时间
try:
driver.get('http://www.sohu.com/')
except TimeoutException:
print('time out after 2 seconds when loading page')
driver.execute_script('window.stop()') #当页面加载时间超过设定时间,通过执行Javascript来stop加载,即可执行后续动作
print('继续执行')
- driver.forward():浏览器向前(点击向前按钮)。
实例:
from selenium import webdriver
wb=webdriver.Firefox()
wb.get('http://www.sohu.com')
wb.get('http://www.youku.com')
wb.get('http://www.qq.com')
wb.back()
wb.forward()
- driver.back():浏览器向后(点击向后按钮)。
实例:
from selenium import webdriver
wb=webdriver.Firefox()
wb.get('http://www.sohu.com')
wb.get('http://www.youku.com')
wb.get('http://www.qq.com')
wb.back()
- driver.refresh():浏览器刷新(点击刷新按钮)。
实例:
from selenium import webdriver
wb=webdriver.Firefox()
wb.get('http://www.sohu.com')
wb.refresh()
- driver.close():关闭当前窗口,或最后打开的窗口。
实例:
from selenium import webdriver
wb = webdriver.Firefox()
wb.get("http://www.sohu.com/")
wb.close()
- driver.quit():关闭所有关联窗口,并且安全关闭session。
实例:
from selenium import webdriver
wb = webdriver.Firefox()
wb.get("http://www.sohu.com/")
wb.quit()
- driver.maximize_window():最大化浏览器窗口。
实例:
from selenium import webdriver
wb = webdriver.Firefox()
wb.get("http://www.sohu.com/")
wb.maximize_window()
- driver.set_window_size(宽,高):设置浏览器窗口大小,参数为长和宽。
实例:
from selenium import webdriver
wb=webdriver.Firefox()
wb.set_window_size(1024,768)
wb.get('http://www.sohu.com')
- driver.get_window_size():获取当前窗口的长和宽。
实例:
from selenium import webdriver
wb=webdriver.Firefox()
wb.set_window_size(800,800)
wb.get('http://www.sohu.com')
print (wb.get_window_size())
运行结果:
{'x': 3, 'height': 800, 'y': 3, 'width': 800}
技术解释:x,y是窗体在winow桌面上的左上角的坐标。
- driver.get_window_position():获取当前窗口坐标。
实例:
from selenium import webdriver
wb=webdriver.Firefox()
wb.set_window_size(800,800)
wb.get('http://www.sohu.com')
print (wb.get_window_position())
运行结果:
{'width': 800, 'y': 3, 'height': 800, 'x': 3}
- driver.get_screenshot_as_file(filename):截取当前窗口,filename参数是保存文件的路径,截图成功返回True,否则是False。
实例:
from selenium import webdriver
wb=webdriver.Firefox()
wb.set_window_size(1024,768)
wb.get('http://www.sohu.com')
wb.get_screenshot_as_file(r'C:pngsohu.png')
技术解释:脚本可以通过设置浏览器窗口大小来测试符合当前分辨率下的测试场景。
- driver.implicitly_wait(秒):隐式等待,通过一定的时长等待页面上某一元素加载完成。若提前定位到元素,则继续执行。若超过时间未加载出,则抛出NoSuchElementException异常。
实例:
from selenium import webdriver
wb = webdriver.Firefox()
wb.implicitly_wait(3)
wb.get("http://www.sohu.com/")
wb.find_element_by_link_text('搜狐首页')
技术解释:当使用了implicitly_wait等待执行测试的时候,如果 WebDriver没有在 DOM中找到元素,将继续等待,超出设定时间后则抛出找不到元素的异常,换句话说,当查找元素或元素并没有立即出现的时候,隐式等待将等待一段时间再查找 DOM,默认的时间是0,一旦设置了隐式等待,则它存在整个 WebDriver对象实例的声明周期中,隐式的等到会让一个正常响应的应用的测试变慢,它将会在寻找每个元素的时候都进行等待,这样会增加整个测试执行的时间。
- driver.switch_to_frame(id或name属性值):切换到新表单(同一窗口)。若无id或属性值,可先通过xpath定位到iframe,再将值传给switch_to_frame(),来切换frame.不得不提到switch_to_frame(),很多人在这样写的时候会发现,这句话被划上了删除线,原因是这个方法已经out了,之后很有可能会不支持,建议的写法是switch_to.frame()
实例:
HTML代码:
<html lang="en">
<head>
<title>FrameSeleniumTest</title>
</head>
<body>
<iframe src="http://www.sohu.com" id="frame" name="mycustomframe"></iframe>
</body>
</html>
Webdriver代码:
from selenium import webdriver
wb = webdriver.Firefox()
wb.switch_to.frame(0) # 用frame的index来定位,第一个是0
# wb.switch_to.frame("frame") # 用id来定位
# wb.switch_to.frame("mycustomframe") # 用name来定位
- driver.switch_to.parent_content():跳出当前一级表单。该方法默认对应于离它最近的switch_to.frame()方法。
实例:
from selenium import webdriver
wb = webdriver.Firefox()
wb.switch_to.frame(0)
switch_to.default_content()
- driver.switch_to.default_content():跳回最外层的页面。
实例:
from selenium import webdriver
wb = webdriver.Firefox()
wb.switch_to.frame(0)
driver.switch_to.default_content()
- driver.switch_to_window(窗口句柄):切换到新窗口。
实例:
from selenium import webdriver
wb = webdriver.Firefox()
wb.get("http://www.sohu.com/")
newwindow = 'window.open("http://www.youku.com");'
wb.execute_script(newwindow)
print(len(wb.window_handles))
second_window_handle=wb.window_handles[1]
wb.switch_to_window(second_window_handle)
#wb.switch_to.window(second_window_handle)
wb.get('http://www.qq.com')
技术解释:Webdirver请求搜狐网之后,通过调用js脚本在Firefox新的窗口中打开优酷网,当前wb对象中保存了两个窗口handle,为了操作js开的窗口,wb调用switch_to_window方法,把wb切换到新窗口中再请求腾讯网。在开发脚本的时候会看到switch_to_window有横线, 针对selenium3 中的窗口定位会自动划掉,不起作用现在换成driver.switch_to.window()
- driver.switch_to_alert():警告框处理。处理JavaScript所生成的alert,confirm,prompt.
实例:Demo.HTML的HTML代码:
<html>
<head>
<title>alert</title>
<script language="javascript"> //JavaScript脚本标注
alert("上联:山石岩下古木枯");//在页面上弹出上联
alert("下联:白水泉边少女妙");//在页面上弹出下联
</script>
</head>
</html>
Webdriver代码:
from selenium import webdriver
wb=webdriver.Firefox()
wb.get(r'demo.html')
alert=wb.switch_to_alert()
alert.accept()
alert.accept()
技术解释:alert,浏览器弹出框,一般是用来确认某些操作、输入简单的text或用户名、密码等,根据浏览器的不同,弹出框的样式也不一样,不过都是很简单的一个小框。在firebug中是无法获取到该框的元素的,也就是说alert是不属于网页DOM树的。针对alert,selenium提供了相应的类来进行处理。selenium.webdriver.common.alert.Alert(driver)。alert的操作有:Alert(driver).accept() # 等同于点击“确认”或“OK”;Alert(driver).dismiss() # 等同于点击“取消”或“Cancel”;Alert(driver).authenticate(username,password) # 验证,针对需要身份验证的alert,目前还没有找到特别合适的示例页面;Alert(driver).send_keys(keysToSend) # 发送文本,对有提交需求的prompt框(上图3);Alert(driver).text # 获取alert文本内容,对有信息显示的alert框.
- driver.execute_script(js):调用js。
实例:
from selenium import webdriver
wb = webdriver.Firefox()
wb.get("http://www.sohu.com")
js="var q=document.documentElement.scrollTop=10000"
wb.execute_script(js)
技术解释:该代码实现打开守护网页,拖动滚动条到底部,实现拖动滚动条到底部是利用js实现。
- driver.get_cookies():获取当前会话所有cookie信息。
实例:
from selenium import webdriver
wb = webdriver.Firefox()
wb.get("http://www.sohu.com/")
print(wb.get_cookies())
运行结果:
[{'path': '/', 'name': 'ad_t_4', 'value': '2', 'httpOnly': False, 'secure': False, 'expiry': None, 'domain': 'www.sohu.com'}, {'path': '/', 'name': 'ad_t_3', 'value': '1', 'httpOnly': False, 'secure': False, 'expiry': None, 'domain': 'www.sohu.com'}, {'path': '/', 'name': 'ad_t_2', 'value': '2', 'httpOnly': False, 'secure': False, 'expiry': None, 'domain': 'www.sohu.com'}, {'path': '/', 'name': 'ad_t_5', 'value': '2', 'httpOnly': False, 'secure': False, 'expiry': None, 'domain': 'www.sohu.com'}, {'path': '/', 'name': 'ad_t_6', 'value': '1', 'httpOnly': False, 'secure': False, 'expiry': None, 'domain': 'www.sohu.com'}, {'path': '/', 'name': 'IPLOC', 'value': 'CN4201', 'httpOnly': False, 'secure': False, 'expiry': None, 'domain': '.sohu.com'}, {'path': '/', 'name': 'SUV', 'value': '1711111631162491', 'httpOnly': False, 'secure': False, 'expiry': None, 'domain': '.sohu.com'}, {'path': '/', 'name': 'beans_dmp_done', 'value': '1', 'httpOnly': False, 'secure': False, 'expiry': None, 'domain': '.sohu.com'}, {'path': '/', 'name': 'beans_new_turn', 'value': '%7B%22sohu-index%22%3A55%7D', 'httpOnly': False, 'secure': False, 'expiry': None, 'domain': 'www.sohu.com'}]
- driver.add_cookie(cookie_dict):添加cookie。“cookie_dict”指字典对象,必须有name和value值。selenium没有修改cookie的功能,只能删除然后添加。
实例:
from selenium import webdriver
wb = webdriver.Firefox()
wb.get("http://www.sohu.com")
wb.add_cookie({'name':'key-aaaaaaa', 'value':'value-bbbb'})
print("1、遍历所有cookie:" )
#遍历cookies 中的name 和value 信息打印,当然还有上面添加的信息
for cookie in wb.get_cookies():
print( "%s:%s" % (cookie['name'], cookie['value']) )
- driver.delete_all_cookies():删除所有cookie信息。
实例:
from selenium import webdriver
wb = webdriver.Firefox()
wb.get("http://www.qq.com/")
wb.delete_all_cookies()
print('删除后的cookie')
print(wb.get_cookies())
- 综合实例
基于前面学习的Webdriver的属性和方法,我们来继续开发Discuz的测试脚本,利用前面学到的知识来增强测试脚本。
- 开发脚本:打开discuz论坛首页并依次打开论坛板块页面并验证链接是否正确
实例:
from selenium import webdriver #加载selenium库
from selenium.webdriver.common.keys import Keys #加载selenium键盘定义库
forum_discuz_app_url='http://192.168.0.110/discuz/forum.php?mod=forumdisplay&fid=2' #Discuz! 程序发布板块链接
forum_discuz_plugin_url='http://192.168.0.110/discuz/forum.php?mod=forumdisplay&fid=38' #Discuz!-插件板块链接
forum_discuz_templet_url='http://192.168.0.110/discuz/forum.php?mod=forumdisplay&fid=39' #Discuz!-模板板块链接
forum_discuz_appcenter_url='http://192.168.0.110/discuz/forum.php?mod=forumdisplay&fid=40' #Discuz!-应用中心板块链接
forum_discuz_install_url='http://192.168.0.110/discuz/forum.php?mod=forumdisplay&fid=41'#Discuz!-安装使用板块链接
forum_discuz_master_url='http://192.168.0.110/discuz/forum.php?mod=forumdisplay&fid=42' #站长帮板块链接
forum_discuz_bug_url='http://192.168.0.110/discuz/forum.php?mod=forumdisplay&fid=43' #Discuz!-BUG反馈板块链接
def get_webdriver(url):
# get_webdriver代码实现在本书第一个webdriver脚本中
def login_discuz(driver,str_user,str_pwd):
#登录代码的实现在本书第一个webdriver脚本
技术解释:以URL为结尾的变量保存了论坛子版块的链接地址,这里用的变量保存,也可以用字典类型保存以上数据,论坛对应论坛链接。如:
frum_urls={
'程序发布':'http://192.168.0.110/discuz/forum.php?mod=forumdisplay&fid=2',
'插件':'http://192.168.0.110/discuz/forum.php?mod=forumdisplay&fid=38',
'模板':'http://192.168.0.110/discuz/forum.php?mod=forumdisplay&fid=39',
'应用中心':'http://192.168.0.110/discuz/forum.php?mod=forumdisplay&fid=40',
'站长帮':'http://192.168.0.110/discuz/forum.php?mod=forumdisplay&fid=41',
'BUG反馈':'http://192.168.0.110/discuz/forum.php?mod=forumdisplay&fid=42'
}
def link_forum_check(driver):
driver.get(forum_discuz_app_url)
if driver.title=='Discuz! 程序发布 - Discuz! Board - Powered by Discuz!':
print('论坛板块Discuz! 程序发布页面打开正确')
driver.get(forum_discuz_plugin_url)
if driver.title=='Discuz!-插件 - Discuz! Board - Powered by Discuz!':
print('论坛板块Discuz!-插件页面打开正确')
driver.get(forum_discuz_templet_url)
if driver.title=='Discuz!-模板 - Discuz! Board - Powered by Discuz!':
print('论坛板块Discuz!-模板页面打开正确')
driver.get(forum_discuz_appcenter_url)
if driver.title=='Discuz!-应用中心 - Discuz! Board - Powered by Discuz!':
print('论坛板块Discuz!-应用中心页面打开正确')
driver.get(forum_discuz_install_url)
if driver.title=='Discuz!-安装使用 - Discuz! Board - Powered by Discuz!':
print('论坛板块Discuz!-安装使用页面打开正确')
driver.get(forum_discuz_master_url)
if driver.title=='站长帮 - Discuz! Board - Powered by Discuz!':
print('论坛板块站长帮页面打开正确')
driver.get(forum_discuz_bug_url)
if driver.title=='Discuz!-BUG反馈 - Discuz! Board - Powered by Discuz!':
print('论坛板块Discuz!-BUG反馈页面打开正确')
if __name__ == '__main__':
#用变量存储用户名,密码
str_user="admin"
str_pwd="admin"
discuz_url=r'http://192.168.0.110/discuz/forum.php'
wb=get_webdriver(discuz_url)
login_discuz(wb,str_user,str_pwd)
link_forum_check(wb)
技术解释:测试用例三要素:测试步骤,输入数据,期望结果,测试脚本同样需要测试步骤,测试数据,预期结果,只有预期结果才能验证结果是否正确,上面脚本代码是请求打开每一个论坛板块的链接地址,然后验证当前打开的页面标题是否是论坛正确的标题,如果是正确的代表页面打开正确。wb.title=='Discuz! 程序发布 - Discuz! Board - Powered by Discuz!',就是验证代码的实现,wb.title是获得请求目标页面后的标题,'Discuz! 程序发布 - Discuz! Board - Powered by Discuz!'是预期结果,通过if语句判断在自动化测试中的结果是否正确。Webdriver把验证discuz页面中的论坛子板块链接打开是否正确的功能封装成link_forum_check(wb)函数。
- 开发脚本:验证导航栏栏目所有栏目链接是否正确打开目标页面
实例:
import time
from selenium import webdriver # 加载selenium库
from selenium.webdriver.common.keys import Keys # 加载selenium键盘定义库
def get_webdriver(url):
# get_webdriver代码实现在本书第一个webdriver脚本中
def login_discuz(driver,str_user,str_pwd):
# login_discuz的实现在本书第一个webdriver脚本
def check_navigation_bar(driver,str_column):
clink_ column = driver.find_element_by_link_text(str_column) # 点击
clink_ column.click()
time.sleep(2)
second_window_handle=driver.window_handles[1]
driver.switch_to_window(second_window_handle)
str_page_title=driver.title
if str_page_title=='云服务器 CVM - 腾讯云':
print('导航栏目:'+str_column+'-页面正常')
if str_page_title=='基于 CentOS 搭建 Discuz 论坛 - 开发者实验室 - 腾讯云':
print('导航栏目:'+str_column+'-页面正常')
if str_page_title=='Discuz! 应用中心':
print('导航栏目:'+str_column+'-页面正常')
if str_page_title=='Discuz! 官方站-PHP 开源论坛 - Powered by Discuz!':
print('导航栏目:'+str_column+'-页面正常')
if str_page_title=='免费建站_Discuz! 专用主机服务_DZ动力':
print('导航栏目:'+str_column+'-页面正常')
time.sleep(2)
driver.close()
mian_window_handle = driver.window_handles[0]
driver.switch_to_window(mian_window_handle)
技术解释:Webdriver脚本中check_navigation_bar的功能是验证导航栏栏目打开链接是否正确的函数,验证的方法主要是通过打开新窗口之后通过浏览器标题来验证。driver.window_handles获得当前firefox打开所有页面窗口的句柄,如果想遍历打印当前浏览器的所有窗体的句柄,可以用下面的代码,
for handle in driver.window_handles:
driver.switch_to_window(handle)
在check_navigation_bar每次打开一个栏目的链接,所以firefox会有两个窗体,第二个打开的窗体就是打开的新链接,mian_window_handle = driver.window_handles[0]保存的就是新打开的导航栏栏目链接窗口的句柄。switch_to_window传入mian_window_handle句柄值之后,driver指针执行了第二个窗口,这时候执行driver.close就是关闭栏目窗口,mian_window_handle = driver.window_handles[0],driver.switch_to_window(mian_window_handle)两行代码作用是driver重新把指针指向主窗口。
if __name__ == '__main__':
# 用变量存储用户名,密码
str_user = "admin"
str_pwd = "admin"
discuz_url = r'http://192.168.0.110/discuz/forum.php'
wb = get_webdriver(discuz_url)
login_discuz(wb, str_user, str_pwd)
time.sleep(2)
check_navigation_bar(wb,'腾讯云主机')
check_navigation_bar(wb, 'Discuz!实验室')
# check_navigation_bar(wb, '服务购买')
check_navigation_bar(wb, '应用中心')
check_navigation_bar(wb, '微社区')
check_navigation_bar(wb, '在线体验')
技术解释:上面实现的Webdriver脚本中涉及一个概念“句柄“。在Windows中,句柄是一个系统内部数据结构的引用。例如当用户操作word文档,其实打开的就是一个窗口,系统会给给这个窗口分配一个数值,系统会通知用户:正在操作的窗口是188号窗口,就此用户实现的应用程序通过这个数值,要求系统对188号窗口进行操作——移动窗口、改变窗口大小、把窗口最小化等等。实际上许多Windows API函数把句柄作为它的第一个参数,如GDI(图形设备接口)句柄、菜单句柄、实例句柄、位图句柄等,不仅仅局限于窗口函数。换句话说,句柄是一种内部代码,通过它能引用受系统控制的特殊元素,如窗口、位图、图标、内存块、光标、字体、菜单等。句柄专业的讲是指使用的一个唯一的整数值,即一个四字节长的数值,来标志应用程序中的不同对象和同类对象中的不同的实例,诸如,一个窗口,按钮,图标,滚动条,输出设备,控件或者文件等,它好比现实生活中的门牌号,只有获得了门牌号,才能找到人的家,才能进门为所欲为。在上面的脚本中driver.switch_to_window(second_window_handle)后执行driver.title,才能得到当前窗体的标题,才能在之后关闭当前页面窗口。这就是因为你获得了句柄,才可以对当前窗口发号施令。
- 开发脚本:添加执行日志,保存执行中页面截图
实例:
import time
import os.path
from selenium import webdriver # 加载selenium库
from selenium.webdriver.common.keys import Keys # 加载selenium键盘定义库
page_correct_title = ['Discuz! 程序发布 - Discuz! Board - Powered by Discuz!',
'Discuz!-插件 - Discuz! Board - Powered by Discuz!',
'Discuz!-模板 - Discuz! Board - Powered by Discuz!',
'Discuz!-应用中心 - Discuz! Board - Powered by Discuz!',
'Discuz!-安装使用 - Discuz! Board - Powered by Discuz!',
'站长帮 - Discuz! Board - Powered by Discuz!',
'Discuz!-BUG反馈 - Discuz! Board - Powered by Discuz!'
]
forum_urls = [
'http://192.168.0.110/discuz/forum.php?mod=forumdisplay&fid=2', #'程序发布':
'http://192.168.0.110/discuz/forum.php?mod=forumdisplay&fid=38', #'插件':
'http://192.168.0.110/discuz/forum.php?mod=forumdisplay&fid=39', #'模板':
'http://192.168.0.110/discuz/forum.php?mod=forumdisplay&fid=40', #'应用中心':
'http://192.168.0.110/discuz/forum.php?mod=forumdisplay&fid=41', # 'Discuz!-安装使用':
'http://192.168.0.110/discuz/forum.php?mod=forumdisplay&fid=41', #'站长帮':
'http://192.168.0.110/discuz/forum.php?mod=forumdisplay&fid=42' # 'BUG反馈':
]
def get_webdriver(url):
# get_webdriver代码实现在本书第一个webdriver脚本中
def login_discuz(driver,str_user,str_pwd):
#登录代码的实现在本书第一个webdriver脚本
技术解释:import time 主要是调用time.sleep方法,为了让脚本执行成功,解决脚本执行完,页面还没有打开或者页面元素还没有出现的问题。import os.path是利用文件夹操作方法完成获得保存截图的文件夹。声明page_correct_title ,forum_urls分别保存了论坛名称和论坛地址,为了后面调用方便。
def link_forum_check(driver, forum_discuz_url, page_correct_title):
driver.get(forum_discuz_url)
if driver.title == page_correct_title:
print(page_correct_title + ':论坛板块页面打开正确')
return True
else:
return False
def save_result_link_screenshot(driver,filename):
dir_path=os.getcwd()
png_filename=time.strftime('%Y-%m-%d %H-%M-%S',time.localtime(time.time()))+'-'+filename+'.png'
screenshot_path=dir_path+'screen'+png_filename
print(screenshot_path)
if driver.save_screenshot(screenshot_path)==True:
print(filename+'页面截图保存成功')
else:
print(filename + '页面截图保存失败')
技术解释:link_forum_check是重新封装了web基本操作第三部分的综合实例A中的脚本link_forum_check函数,link_forum_check传入三个参数driver, forum_discuz_url, page_correct_title,分别是webdriver实例,discuz子论坛链接地址,打开目标论坛板块页面的预期正确标题。save_result_link_screenshot(driver,filename)作用是保存页面截图,driver是webdirver实例,filename是页面截图的文件名称。截图文件的文件名称规范是执行日期+时间+传入的页面截图文件名称.png。分析源码driver.save_screeshot只能保存后缀为png文件,跟踪save_screeshot函数调get_screenshot_as_file,内部源码实现有这一段:
if not filename.lower().endswith('.png'):
warnings.warn("name used for saved screenshot does not match file "
"type. It should end with a `.png` extension", UserWarning)
改段代码lower是把文件名都变成小写,if语句判断文件后缀不是png的时候,提示文件后缀必须是png。
if __name__ == '__main__':
# 用变量存储用户名,密码
str_user = "admin"
str_pwd = "admin"
discuz_url = r'http://192.168.0.110/discuz/forum.php'
wb = get_webdriver(discuz_url)
login_discuz(wb, str_user, str_pwd)
for i in range(0, 7):
if link_forum_check(wb, forum_urls[i], page_correct_title[i]):
save_result_link_screenshot(wb,page_correct_title[i])
技术解释:入口函数分别调用获得打开论坛主页的webdriver的firefox实例,然后执行登录,验证论坛板块打开链接是否正常。for i in range(0, 7)是遍历论坛板块,range中7可以改由len(forum_urls)来获得论坛子版块链接数会更高效,下次修改代码只要修改forum_urls列表中的数据即可。