50.[Python]使用Selenium包做Web页面自动化测试详解 - Go语言中文社区

50.[Python]使用Selenium包做Web页面自动化测试详解


载*请注明原始出处:http://blog.csdn.net/a464057216/article/details/52717464

简介

Selenium是ThoughtWorks公司为Web自动化测试开发的工具,除支持多种操作系统如Linux、Mac OS X、Windows外,还支持Chrome、Firefox、Safari、Opera、IE等多种浏览器,适合做Web应用的兼容性测试及自动化测试。在Web开发的持续集成中,如果每次迭代都采用手工方式对已有的功能进行回归测试,需要的人力、时间成本将是非常巨大的,采用Selenium进行自动化地回归测试,可以让团队的精力集中在新功能的测试上,提升工作效率。

本文主要介绍在Python中使用Selenium包进行自动化测试的方法,您需要具备一些HTML的知识及搭建Web服务器的方法,这样才能在学习的过程中亲手做实验,另外完整的示例代码放在我的github项目上,欢迎大家访问。首先使用pip install selenium命令安装Python的Selenium包。针对不同的浏览器及其版本使用Selenium的方法会有所不同,如下是我的版本信息:

  • 操作系统:Mac OS Sierra
  • Python: 2.7.10
  • Selenium: 2.53.6
  • Firefox: 47.0.1
  • Safari: 10.0
  • Opera: 39.0
  • Chrome: 53

使用方法

访问页面

Selenium运行自动化测试的方法是打开浏览器,按照脚本规定的步骤模拟人的操作,如点击按钮、在文本框输入文本等,然后检查期望结果。第一步就是要知道访问什么页面,比如测试使用Firefox访问百度:

# -*- coding: utf-8 -*-

from selenium import webdriver
from selenium.webdriver.common.keys import Keys

# 目前支持的driver有Firefox, Chrome, IE和Remote等
driver = webdriver.Firefox(executable_path="./geckodriver")
# 直到页面被加载完(onload事件被触发)才将控制权返回脚本
driver.get('https://www.baidu.com')
assert u'百度一下,你就知道' in driver.title
print u"当前URL:", driver.current_url

对于使用了大量AJAX的页面,webdriver并不知道页面何时真正加载完成,这时需要使用waits告诉Selenium等待时间。

Webdriver是Selenium能够实现跨浏览器自动化测试的关键,通常简称wd。不同浏览器有不同的wd,但对外提供了统一的接口调用各个浏览器自身的自动化测试接口。上面Firefox的webdriver请在此下载

定位元素

打开页面后,第二件事情就是定位到我们要操作的页面元素,定位单个页面元素有如下方法(下面的方法,如果没有定位到相应的元素,抛出NoSuchElementException异常):

  • find_element_by_id:通过id属性定位元素(返回第一个匹配的)。
  • find_element_by_name:通过name属性定位元素(返回第一个匹配的)。
  • find_element_by_xpath:通过xpath定位元素(返回第一个匹配的)。
  • find_element_by_link_text:通过超链接文本定位超链接元素,必须是完全匹配(返回第一个匹配的)。
  • find_element_by_partial_link_text:通过超链接文本定位超链接元素,可以是部分匹配(返回第一个匹配的)。
  • find_element_by_tag_name:通过标签名字定位元素(返回第一个匹配的)。
  • find_element_by_class_name:通过class属性定位元素(返回第一个匹配的)。
  • find_element_by_css_selector:使用CSS选择器语法定位元素(返回第一个匹配的)。

上面的方法,对应的批量定位方法如下(返回对应网页元素的列表):

  • find_elements_by_name
  • find_elements_by_xpath
  • find_elements_by_link_text
  • find_elements_by_partial_link_text
  • find_elements_by_tag_name
  • find_elements_by_class_name
  • find_elements_by_css_selector

此外,webdriver还有find_elementfind_elements方法定位元素:

from selenium.webdriver.common.by import By

driver.find_element(By.XPATH, '//button[text()="Some text"]')
driver.find_elements(By.XPATH, '//button')

By对象含有如下属性:

  • ID :元素id属性。
  • XPATH:xpath。
  • LINK_TEXT :超链接文本。
  • PARTIAL_LINK_TEXT :部分超链接文本。
  • NAME :元素name属性。
  • TAG_NAME :元素标签名字。
  • CLASS_NAME :元素class属性。
  • CSS_SELECTOR :使用CSS元素选择器语法。

操作文本框元素

比如在百度的输入框查询信息:

elem = driver.find_element_by_name('wd')

# 清空预填充内容
elem.clear()
# Keys对象表示键盘按键,如F1、ALT、ARROW_DOWN等
elem.send_keys(u'48.HTTP基本认证与摘要认证', Keys.RETURN)

assert u'Mars Loo的博客' in driver.page_source

# quit方法关闭整个浏览器
driver.quit()

上传文件也可以采用send_keys方法,此时参数提供为文件的路径即可。

操作表单元素

定位到一个表单元素后,可以调用其submit方法提交表单:

form = ff.find_element_by_name('survey')
form.submit()

如果调用submit方法的元素不是表单,抛出异常NoSuchElementException。提交表单也可以定位到提交按钮元素后,调用其click方法:

submit = ff.find_element_by_id('submit')
submit.click()

操作select元素

# 采用xpath获取第一个select元素(即使有多个也返回第一个)
element = ff.find_element_by_xpath("//select[@name='car']")
# 获取所有选项,打印选项值并点击
all_options = element.find_elements_by_tag_name("option")
for option in all_options:
    print "Value is: %s" % option.get_attribute("value")
    option.click()

Selenium还提供了对<select>元素的抽象——Select对象:

from selenium.webdriver.support.ui import Select

select = Select(ff.find_element_by_xpath("//select[@name='car']"))
select.select_by_visible_text("infinity")
select.select_by_value("bmw")
# 按照option的索引选择,第一个option的index为0
select.select_by_index("0")

对于允许多选以及有预选值的场景,可以做如下处理:

select = Select(ff.find_element_by_xpath("//select[@name='car']"))
# 获取所有已选项
print select.all_selected_options
# 去选所有选项
select.deselect_all()
# 获取所有可选项
print select.options

拖放元素

selenium包目前还不支持HTML 5的拖放,一种解决方案是通过Javascript脚本及jQuery解决:

jquery_url = "http://upcdn.b0.upaiyun.com/libs/jquery/jquery-2.0.2.min.js"

ff = webdriver.Firefox(executable_path="./geckodriver")
ff.get('http://html5demos.com/drag')

ff.set_script_timeout(30)
with open("load_jquery.js") as f:
    load_jquery_js = f.read()
with open("drag_and_drop.js") as f:
    drag_and_drop_js = f.read()
ff.execute_async_script(load_jquery_js, jquery_url)

ff.execute_script(drag_and_drop_js +
                  "$('#one').simulateDragDrop({dropTarget:'#bin'});")

load_jquery.jsdrag_and_drop.js请在我的github项目获取。

在窗口和frame间切换

在浏览器不同窗口之间切换的方法如下:

a = ff.find_element_by_tag_name("a")

# 保存原始窗口,window_handlers是目前wd打开的所有窗口的句柄列表
prev = ff.window_handles[-1]

# 点击超链接(targe="_blank")后,浏览器新窗口被激活
a.click()

# 保存新窗口
new = ff.window_handles[-1]

# 切换到原始窗口
ff.switch_to_window(prev)
print "Switch to prev success"

# 切换到新窗口
ff.switch_to_window(new)
print "Switch to new success"

因为HTML 5中不再建议使用<frameset>标签,所以以<iframe>为例说明如何在框架间切换:

ff.switch_to_frame('frame1')
element = ff.find_element_by_name('wd')
element.clear()
element.send_keys("This is a test")

# 切换到顶级frame,然后才可以切换到其他iframe
ff.switch_to_default_content()
ff.switch_to_frame('frame2')
element = ff.find_element_by_name('email')
element.clear()
element.send_keys("Input to Sohu")

处理弹窗

Javascript的角度,弹窗分为三种:alert、confirm和prompt。使用webdriver的switch_to_alert方法可以将焦点切换到当前的弹窗上,并返回一个Alert对象。
如果是alert类型的窗口,点击取消或者确定都可以关闭该窗口:

# 切换到当前弹出框并返回Alert对象
alert = ff.switch_to_alert()
# 获取弹出框的文本内容
print "Alert text:", alert.text
# 点击弹出框的确定按钮
alert.accept()
# 点击弹出框的取消按钮
# alert.dismiss()

如果是confirm类型的窗口,点击确定会给Javascript返回true,点击取消会给javascript返回false。如果是prompt类型的窗口,可以输入一段文本后再点击取消还是确定,如果点击取消会给Javascript返回null,如果点击确定会给Javascript返回输入的文本,因为弹出的prompt框默认会将其中可编辑的文本内容覆盖选中(见下图),所以Alert对象没有clear方法:

这里写图片描述

alert = ff.switch_to_alert()

# 如果是prompt类型的弹出框,直接向其中输入内容
alert.send_keys("This is my choice")
# 然后点击prompt框的确定按钮
alert.accept()

处理HTTP基本认证及摘要认证

HTTP基本认证和摘要认证的基本原理可以参考我的博文,这种认证弹窗不需要按照上面的方法处理,如果是Firefox浏览器,可以在浏览器中访问about:config进入配置界面将browser.safebrowsing.malware.enabled设置为true,此时Firefox就允许我们以scheme://username:password@domain/path?query#segment的形式访问网页了。设置好了以后,Selenium的脚本可以这样写:

ff = webdriver.Firefox(executable_path="./geckodriver")
ff.get('http://mars:loo@localhost:5000/')

历史记录前进及后退

通过webdriver的backforward方法,可以很方便地在历史记录中前进及后退:

a = ff.find_element_by_tag_name('a')
a.click()
print "Go back"
ff.back()
print "Go forward"
ff.forward()

处理cookie时,需要webdriver先访问到合法的cookie域,添加cookie然后重新访问即可,比如(namevalue字段分别表示cookie的键和值):

 ff.get('http://localhost:5000/')
 ff.add_cookie({'name': 'username', 'value': 'marsloo'})
 ff.get('http://localhost:5000/')
 # 获取当前域的cookies
 print ff.get_cookies()

上述代码的输出可能为:

[{u'domain': u'localhost', u'name': u'username', u'value': u'marsloo', u'expiry': None, u'path': u'', u'httpOnly': False, u'secure': False}]

等待元素加载

如果请求的页面使用了大量的AJAX,Selenium不会等待其请求完成后再将控制权交给脚本。为了正确定位到所需的元素,需要使用Selenium的等待功能,最简单的是这样:

# 对于所有元素获取,会等待3秒
ff.implicitly_wait(3)
try:
    a = ff.find_element_by_partial_link_text('g')
    a.click()
except NoSuchElementException:
    print "Page load fail or no such element"

在每一个浏览器会话中只需要调用一次implicitly_wait方法。如果相对每个元素获取设置不同的超时时间,可以这样做(其他更详细的代码可以在我的github项目获取):

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException


@auto_close
def func():
    try:
        a = WebDriverWait(ff, 3).until(
            EC.presence_of_element_located((By.TAG_NAME, "p")))
        a.click()
    except TimeoutException:
        print "No such element"

上述代码中的(By.TAG_NAME, "p")元组,在下面的解释中以locator参数代替。expected_conditions对象还有很多方法可以使用:

  • title_is(title):标题是title(完全匹配)。
  • title_contains(title):标题包含title(部分匹配)。
  • presence_of_element_located(locator):元素在DOM树上存在(但是不一定可见)。
  • visibility_of_element_located(locator):元素在DOM树上存在并且肉眼可见。
  • visibility_of(element):检查元素的肉眼可见性。
  • presence_of_all_elements_located(locator):元素们在DOM树上存在(但是不一定可见),locator返回的是元素的列表。
  • text_to_be_present_in_element(locator, text):某个元素的文本中是否包含text
  • text_to_be_present_in_element_value(locator, text):某个元素的value属性中是否包含text
  • frame_to_be_available_and_switch_to_it(locator):判断某个frame是否可操作,如果可以的话切换到该frame并返回True,否则返回False
  • invisibility_of_element_located(locator):元素不存在于DOM树或者不可见。
  • element_to_be_clickable(locator) :元素可见且是可点击状态。
  • staleness_of(element):元素从DOM树上移除。
  • element_to_be_selected(element):元素被选中。
  • element_located_to_be_selected(locator):元素被选中。
  • element_selection_state_to_be(element, bool):判断元素被选中的状体,bool参数中True表示选中,False表示未选中。
  • element_located_selection_state_to_be(locator, bool):判断元素被选中的状体,bool参数中True表示选中,False表示未选中。
  • alert_is_present:弹出了一个窗口。

使用其他浏览器

Chrome

使用Chrome进行测试,需要先下载Chrome Driver (可能需要翻墙),解压缩后代码如下:

driver = webdriver.Chrome(executable_path="./chromedriver")

Opera

首先下载Opera Driver,解压缩后代码如下:

webdriver_service = service.Service('./operadriver')
webdriver_service.start()
driver = webdriver.Remote(webdriver_service.service_url,
                          webdriver.DesiredCapabilities.OPERA)

driver.get('https://www.baidu.com')

Safari

Safari 10已经开始内置自动化测试的支持,Safari偏好设置->高级->在菜单栏中显示“开发”菜单,然后开发->允许远程自动化,最后下载Selenium Server的jar包,测试脚本如下:

# -*- coding: utf-8 -*-

from selenium import webdriver
import time
import os

os.environ["SELENIUM_SERVER_JAR"] = "selenium-server-standalone-2.53.1.jar"

# 设置quiet模式,否则会打印很多log
driver = webdriver.Safari(quiet=True)
driver.get('https://www.baidu.com')
assert u'百度一下,你就知道' in driver.title
print u"当前URL:", driver.current_url
time.sleep(4)
driver.quit()

保存屏幕截图

使用driver.save_screenshot(filename)可以保存浏览器的运行截图(默认是覆盖写入)。

滚动至屏幕底部

使用ff.execute_script("window.scrollTo(0, document.body.scrollHeight);")可以滚动至屏幕底部。

如果觉得我的文章对您有帮助,欢迎关注我(CSDN:Mars Loo的博客)或者为这篇文章点赞,谢谢!

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/a464057216/article/details/52717464
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-03-07 21:29:44
  • 阅读 ( 1351 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢