web自动化测试的工具
现在主流的web测试工具我们常用的就是selenium的那一套工具包括
- 浏览器一般选择chrome
- 浏览器对应的driver(chromedriver)
- Python
- Selenium库
web自动化测试的环境安装
Selenium提供了一个webdriver_manager库,可以帮助自动下载和更新Chrome浏览器的驱动程序chromedriver.exe。您可以按照以下步骤操作:
安装webdriver_manager库。可以在命令行或终端中运行以下命令进行安装:
```
pip install webdriver_manager
1 2 3 4 5 6 7 8 9 10
| 在Selenium Python脚本中,导入webdriver_manager并使用ChromeDriverManager类来创建ChromeDriver实例。这样,如果您的Chrome浏览器版本发生变化,webdriver_manager将自动下载合适的chromedriver版本,并使用它来运行测试。
```python from selenium import webdriver from webdriver_manager.chrome import ChromeDriverManager # 创建ChromeDriver实例 driver = webdriver.Chrome(executable_path=ChromeDriverManager().install()) # 在浏览器中打开网页 driver.get('https://www.baidu.com/')
|
安装selenium包
Selenium WebDriver是一个第三方模块,并不是Python的标准模块,所以在导入这个模块之前,还需要将这个第三方模块安装到Python的目录中,这样才能使用import或者from…import语句进行导入。
安装最新版本:
安装指定版本:
1
| pip install selenium==版本号
|
卸载selenium
web自动化测试脚本编写的基本步骤
1
| from selenium import webdriver
|
创建驱动浏览器对象并启动浏览器
1 2 3
| 浏览器驱动对象 = webdriver.Firefox() 浏览器驱动对象 = webdriver.Chrome() 浏览器驱动对象 = webdriver.Edge()
|
编写自动化执行步骤
打开网页:
浏览器驱动对象.get(“网页链接”)
浏览器窗口最大化:
关闭驱动对象
刷新窗口
使用 JavaScript 中的 window.scrollTo()
方法来实现滚动
滚动到底部:
1
| driver.execute_script("window.scrollTo(0, 0);")
|
滚动到页面中间:
1
| driver.execute_script(f"window.scrollTo(0, {middle});")
|
滚动到页面顶部:
1
| driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
|
缓慢滚动到底部:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| height = driver.execute_script("return document.body.scrollHeight")
scroll_speed = 500 interval = 0.01 while True: driver.execute_script(f"window.scrollBy(0, {scroll_speed});") time.sleep(interval)
new_height = driver.execute_script("return document.body.scrollHeight") if new_height == height: break height = new_height
|
八大元素
- id
- name
- class_name(使用元素的class属性)
- xpath(基于文件路径)
- css(元素选择器定位)
- tag_name(元素的标签名称)
- link_text(定位超链接,a标签)
- partail_link_text(定位超链接,a标签 模糊)
1 2 3 4 5 6
| 汇总: 1.基于元素属性特有定有定位:id/name/class_name 2. 基于元素标签名称定位:tag_name 3. 定位超链接文本:link_text/partail_link_text 4. 基于元素路径定位:xpath 5. 基于选择器:css
|
1 2 3 4 5 6 7 8 9 10 11
| """底层代码""" from selenium.webdriver.common.by import By
ID = "id" XPATH = "xpath" LINK_TEXT = "link text" PARTIAL_LINK_TEXT = "partial link text" NAME = "name" TAG_NAME = "tag name" CLASS_NAME = "class name" CSS_SELECTOR = "css selector"
|
1、id
HTML规定id属性在HTML文档中必须是唯一的
driver.find_element(By.ID, “id名”).send_keys(“输入内容”)
1 2 3 4 5 6 7
| driver = webdriver.Chrome()
driver.maximize_window() driver.get("https://www.baidu.com/") driver.find_element(By.ID, "kw").send_keys("输入的内容") driver.find_element(By.ID, "su").click() sleep(3)
|
2、name
1
| driver.find_element(By.NAME, "wd").send_keys("输入的内容")
|
1 2 3 4 5 6
| driver = webdriver.Chrome() driver.get('https://www.baidu.com')
driver.find_element(By.NAME, 'wd').send_keys("软件测试老白")
driver.find_elements(By.NAME, 'wd')[0].send_keys("软件测试老白")
|
3、class_name
1
| driver.find_element(By.CLASS_NAME,"login-top")
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| driver = webdriver.Chrome()
driver.maximize_window() driver.get("https://www.bilibili.com/")
driver.find_element(By.CLASS_NAME,'icon-bg icon-bg__channel').click()
sleep(3)
|
4、tag_name
通过标签名来定位,一般很少使用
如果页面中存在多个相同的标签名,默认返回第一个
1
| driver.find_element(By.TAG_NAME,"input").send_keys("admin")
|
5、link_text
精确匹配超链接
1
| driver.find_element(By.LINK_TEXT,"新闻").click()
|
1 2 3
| driver = webdriver.Chrome() driver.get('https://www.baidu.com') driver.find_element(By.LINK_TEXT, '新闻').click()
|
6、partail_link_text
模糊匹配超链接
1 2 3
| driver = webdriver.Chrome() driver.get('https://www.baidu.com') driver.find_element(By.PARTIAL_LINK_TEXT, '闻').click()
|
7、xpath
如果要定位的元素没有id、name、class属性,该如何进行定位?
——使用xpath。
xPath是XML Path的简称,是一门在XML文档中查找元素信息的语言
XML是一种标记语言。
● 本质是一种查询语言
●支持逻辑运算、函数
● 实现非常强大的功能
● 可以用于APP自动化测试
依赖于元素的路径:
● 绝对路径:/开头是绝对路径
● 相对路径://开头是相对路径
Xpath可以通过相对路径与绝对路径去定位元素,绝对路径从 HTML 根节点开始算,相对路径从任意节点开始。
说明 |
举例 |
从根节点开始选取(绝对路径) |
/html/div/ |
从任意节点开始选取(相对路径) |
//div,列举出所有div标签 |
选取当前节点的父节点 |
//input/.. 会选取 input 的父节点 |
选取属性,或者根据属性选取 |
|
使用id属性定位 |
//div[@id=’id_value’] |
使用class属性定位 |
//a[@class=”mnav”] |
使用name属性定位 |
//div[@name=’wd’] |
多个属性定位 |
//input[@name=”wd” and @class=”s_ipt”] |
第n个元素,使用index定位 |
//div[@id=”s-top-left”]/a[3] |
最后一个元素 |
//a[@class=”mnav”] [last()] |
属性包含某字段 |
//div[contains(@title,’text’)] |
属性以某字段开头 |
//div[starts-with(@title,’text’)] |
属性以某字段结尾 |
//div[ends-with(@title,’text’)] |
文本包含 |
//a[contains(text(),”网盘”)] |
文本等于 |
//span[text() = “菜单”] |
同级弟弟元素 |
//div[@id==’id’]/following-sibling::div |
同级哥哥元素 |
//div[@id==’id’]/preceding-sibling::div |
●使用绝对路径,一般不推荐绝对路径,因为写起来太麻烦了
1 2 3 4 5
| driver = webdriver.Chrome() driver.get('https://www.baidu.com')
driver.find_element(By.XPATH, '/html/body/div/div/div[3]/a') time.sleep(2)
|
下面使用相对路径定位
●使用id属性定位
1 2 3
| driver = webdriver.Chrome() driver.get('https://www.baidu.com') driver.find_element(By.XPATH, '//input[@id="kw"]').send_keys("软件测试老白")
|
●使用class属性定位
1 2 3 4
| driver = webdriver.Chrome() driver.get('https://www.baidu.com') # class中间有空格,需要全部写上 driver.find_element(By.XPATH, '//a[@class="mnav c-font-normal c-color-t"]')
|
●根据name属性定位
1 2 3
| driver = webdriver.Chrome() driver.get('https://www.baidu.com') driver.find_element(By.XPATH, '//input[@name="wd"]').send_keys("软件测试老白")
|
●由子元素定位父元素
1 2 3 4
| driver = webdriver.Chrome() driver.get('https://www.baidu.com')
driver.find_element(By.XPATH, '//input[@id="kw"]/..')
|
●多个属性组合定位 支持and or
1 2 3
| driver = webdriver.Chrome() driver.get('https://www.baidu.com') driver.find_element(By.XPATH, '//input[@name="wd" and @class="s_ipt"]').send_keys("软件测试老白")
|
●多组数据使用下标定位
1 2 3 4
| driver = webdriver.Chrome() driver.get('https://www.baidu.com')
driver.find_element(By.XPATH, '//div[@id="s-top-left"]/a[3]')
|
●根据文本内容定位
1 2 3 4
| driver = webdriver.Chrome() driver.get('https://www.baidu.com')
driver.find_element(By.XPATH, '//a[contains(text(),"网盘")]')
|
●其它定位方式,轴定位
1 2 3 4 5 6 7
| driver = webdriver.Chrome() driver.get('https://www.baidu.com')
driver.find_element(By.XPATH, '//a[@class="mnav c-font-normal c-color-t"][last()]') 动态ID https://element.eleme.cn/ driver.find_element(By.XPATH, '//span[text() = "默认 click 触发子菜单"]/following-sibling::div/div/input')
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| driver.find_element(By.XPATH,"//form/span/input").send_keys("输入的内容")
driver.find_element(By.XPATH,"//input[@autocomplete='off']").send_keys("输入的内容")
driver.find_element(By.XPATH,"//*[@autocomplete='off']").send_keys("输入的内容") driver.find_element(By.XPATH,"//*[@*='off']").send_keys("输入的内容")
driver.find_element( By.XPATH,"//input[starts-with(@autocomplete,'of')]" ).send_keys("输入的内容") driver.find_element( By.XPATH,"//input[substring(@autocomplete,2)='ff']" ).send_keys("输入的内容")
driver.find_element( By.XPATH,"//input[contains(@autocomplete,'of')]" ).send_keys("输入的内容")
value = driver.find_element( By.XPATH,"//span[text()='按图片搜索']" ).get_attribute('class')
|
1、XPATH的语法
1 2 3 4 5 6 7 8 9 10 11
| 语法:表示层级+属性 /(开头)表示根路径 /html/body/div //(任意层级)(包括下级、下级的下级…) //div @属性 ‘//a[@target=“_top”]’ /(中间)表示下一级 ‘//p//input’ .表示本级 …表示上一级
|
2、 XPATH的函数
1 2 3 4 5 6 7 8 9
| 函数是XPATH另一个魅力,常用的函数: ● text:获取元素内的文本 ● contains:任意位置包含匹配 ● starts-with:开头 ● substring:截取 $x(“//a[text=(vivoX5MAX L 移动4G 八核超薄大屏5.5双卡)]”) $x(“//a[contains( text=(),‘vivo’) ]”) $x(“//a[start-with( text=(),‘vivo’) ]”) $x(“//a[substring(@name,2)=‘vi’ ]”)
|
8、css
1. id选择器
- 前提:元素必须有id属性
- 语法:#id 如:#password
2. class选择器
- 前提:元素必须是有class属性
- 语法:.class 如:.telA
3. 元素选择器
4. 属性选择器
5. 层级选择器
- 语法:
p>input
p input
- 提示:
大于号和空格的区别,大于号必须为子元素,空格则不用
6. 【css延伸】
input[type^=‘p’] type属性以p字母开头的元素
input[type$=‘d’] type属性以d字母结束的元素
input[type*=‘w’] type属性包含w字母的元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| driver.find_element(By.CSS_SELECTOR, "#userA").send_keys("admin")
driver.find_element(By.CSS_SELECTOR, "[name='passwordA']").send_keys("123456")
driver.find_element(By.CSS_SELECTOR, ".telA").send_keys("18611112222")
span = driver.find_element(By.CSS_SELECTOR, "span").text print("获取的span标签文本值为:",span)
driver.find_element( By.CSS_SELECTOR,"p>input[placeholder='电子邮箱A']" ).send_keys("123@qq.com")
|
XPATH和CSS类似功能对比
定位方式 |
XPath |
CSS |
元素名 |
//input |
input |
id |
//input[@id=‘userA’] |
@userA |
class |
//*[@class=‘telA’] |
.telA |
属性 |
1.//*[text()==“xxx”] 2.//input[starts-with(@attribute,‘xxx’)] 3.//input[contains(@attribute,‘xxx’)] |
1.input[type^=‘p’] 2.input[type$=‘d’ 3.input[type*=‘w’] |
通过css_selector定位,By.CSS_SELECTOR
当一个元素无法直接定位,也就是没有id,name等确定标识,这个时候我们需要考虑使用css selector定位器。
它是一种通过CSS样式选择器来定位元素的方法
CSS常用汇总
●使用id属性定位,id前面要加#号
1 2 3
| driver = webdriver.Chrome() driver.get('https://www.baidu.com') driver.find_element(By.CSS_SELECTOR, '#kw').send_keys("软件测试老白")
|
●通过class属性定位,class前面要加.
1 2 3
| driver = webdriver.Chrome() driver.get('https://www.bilibili.com') driver.find_element(By.CSS_SELECTOR, '.nav-search-input').send_keys('软件测试老白')
|
●根据name属性定位,属性值为[name=”wd”]
1 2 3
| driver = webdriver.Chrome() driver.get('https://www.baidu.com') driver.find_element(By.CSS_SELECTOR, '[name="wd"]').send_keys("软件测试老白")
|
●根据标签属性定位
1 2 3 4 5 6 7 8 9
| driver = webdriver.Chrome() driver.get('https://www.baidu.com') driver.find_element(By.CSS_SELECTOR, 'a[href="http://image.baidu.com/"]').click()
driver.find_element(By.CSS_SELECTOR, 'a[href*="image.baidu.com"]').click()
driver.find_element(By.CSS_SELECTOR, 'a[href^="http://image.baidu"]').click()
driver.find_element(By.CSS_SELECTOR, 'a[href$="image.baidu.com/"]').click()
|
●组合定位
1 2 3 4 5 6
| driver = webdriver.Chrome() driver.get('https://www.baidu.com')
driver.find_element(By.CSS_SELECTOR, 'input[name="wd"]').send_keys("软件测试老白")
driver.find_element(By.CSS_SELECTOR, 'input.s_ipt').send_keys("软件测试老白")
|
●定位子元素
一般根据最近一个id属性往下找,可以根据class或者标签。
#s-top-left > a
:nth-child(3)代表第几个子元素,下标从1开始
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| driver = webdriver.Chrome() driver.get('https://www.baidu.com')
driver.find_element(By.CSS_SELECTOR, 'div.s-top-left-new.s-isindex-wrap a' ) driver.find_element(By.CSS_SELECTOR, 'div#s-top-left a') driver.find_element(By.CSS_SELECTOR, '#s-top-left a')
driver.find_element(By.CSS_SELECTOR, '#s-top-left a:nth-child(3)') driver.find_elements(By.CSS_SELECTOR, '#s-top-left a')[2]
driver.find_element(By.CSS_SELECTOR, '#s-top-left a:first-child')
driver.find_element(By.CSS_SELECTOR, '#s-top-left a:last-child')
|
当然 css selector选择器不止这些用法,还有这里只列举了一些重要的用法,当页面元素过多时,我们一般都会直接通过浏览器的复制功能。
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| # step1:导入Selenium WebDriver模块 from selenium import webdriver import time for i in range(1,10): #step2: 创建驱动浏览器对象并启动浏览器 driver = webdriver.Firefox() #step3: 编写自动化执行步骤 driver.maxmimize() driver.get("https://www.baidu.com") #清空文本框 driver.find_element_by_xpath("//input[@id='su']").clear() # 输入内容 driver.find_element_by_xpath("//input[@id='su']").send_keys("百度一下") # 点击 driver.find_element_by_xpath("//input[@name='ss']").click() time.sleep(5) # step4: 关闭驱动对象 driver.quit()
|
针对海盗商城的登录功能进行自动化测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| import time from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.select import Select
chrome = webdriver.Chrome() chrome.implicitly_wait(10) chrome.maximize_window() chrome.get('http://129.211.129.101:9007/index.php?m=user&c=public&a=login')
chrome.find_element(By.ID, 'username').send_keys('XieChuang') chrome.find_element(By.ID, 'password').send_keys('123456')
chrome.find_element(By.CLASS_NAME, 'login_btn').click()
time.sleep(3)
chrome.find_element(By.LINK_TEXT, '进入商城购物').click()
chrome.find_element(By.NAME, 'keyword').send_keys('小米6') chrome.find_element(By.CLASS_NAME, 'btn1').click()
chrome.find_element(By.XPATH, '/html/body/div[3]/div[2]/div[3]/div[2]/div[1]/a/img').click()
new_window = chrome.window_handles[-1]
chrome.switch_to.window(new_window)
chrome.find_element(By.ID, 'joinCarButton').click()
chrome.find_element(By.CLASS_NAME, 'other_join').click()
chrome.find_element(By.CSS_SELECTOR, '.shopCar_btn_03.fl').click()
chrome.find_element(By.CLASS_NAME, 'add-address').click()
chrome.find_element(By.NAME, 'address[address_name]').send_keys('XC') chrome.find_element(By.NAME, 'address[mobile]').send_keys('15910100202')
sheng=chrome.find_element(By.ID,'add-new-area-select') Select(sheng).select_by_visible_text('北京市')
shi=chrome.find_elements(By.CLASS_NAME,'add-new-area-select')[1] Select(shi).select_by_visible_text('北京市') qu=chrome.find_elements(By.TAG_NAME,'select')[2] Select(qu).select_by_visible_text('海淀区') chrome.find_element(By.NAME, 'address[address]').send_keys('迈行大厦') chrome.find_element(By.NAME, 'address[zipcode]').send_keys('100000') chrome.find_element(By.CLASS_NAME,'aui_state_highlight').click()
|
元素定位最佳顺序
1.ID(唯一标识):如果元素具有唯一的ID属性,优先使用ID进行定位,因为它是最快和最可靠的定位方式。
2.CSS选择器:如果元素没有唯一的ID,可以考虑使用CSS选择器进行定位。CSS选择器具有灵活的语法,并且在性能上通常比XPath更高效。
3.类名(class):如果元素没有唯一的ID或合适的CSS选择器,可以使用元素的类名进行定位。尽量选择具有明确含义的类名,并避免选择过于通用的类名。
4.Name属性:如果元素具有唯一的name属性,可以使用name属性进行定位。但要注意,name属性并不是所有元素都具有的,所以不是一种通用的定位方式。
5.XPath:如果前面的方式都无法定位元素,可以使用XPath进行定位。XPath提供了强大的定位功能,但在性能上相对较低,因为XPath需要遍历整个文档,直到找到匹配的元素。暂无性能对比数据,我觉得好用就用,及时行乐。
6.标签名(TagName):如果元素无法使用上述方式进行定位,可以考虑使用标签名进行定位。但要注意,标签名定位方式通常会返回多个匹配的元素,需要结合其他条件来缩小范围。
框
单选框
1
| driver.find_element(By.XPATH,'//span[text()="Android"]').click()
|
多选框
例子:选中 香蕉 苹果 西瓜:
1 2 3
| driver.find_element(By.XPATH,'//span[text()="香蕉"]').click() driver.find_element(By.XPATH,'//span[text()="苹果"]').click() driver.find_element(By.XPATH,'//span[text()="西瓜"]').click()
|
下拉框
1 2
| select=Select(driver.find_element(By.ID, 's2')) select.select_by_index(1)
|
弹性框
确定弹窗
1 2 3 4 5 6
| driver.find_element(By.NAME,'b1').click()
print(driver.switch_to.alert.text)
driver.switch_to.alert.accept()
|
确定取消
1 2 3 4 5 6 7 8
| driver.find_element(By.NAME,'b1').click()
print(driver.switch_to.alert.text)
driver.switch_to.alert.accept()
driver.switch_to.alert.dismiss()
|
输入确定
1 2 3 4 5 6
| driver.find_element(By.NAME,'b1').click()
driver.switch_to.alert.send_key("123")
driver.switch_to.alert.accept()
|
iframe框
1 2 3 4 5 6
| iframe_element = driver.find_element_by_css_selector("iframe-selector")
driver.switch_to.frame(iframe_element)
driver.switch_to.default_content()
|
如果在 iframe 上下文中完成了操作,想要切回到默认的上下文,可以使用 switch_to.default_content
方法。
1 2 3 4 5 6 7 8 9 10
| driver.switch_to.frame(0)
driver.switch_to.default_content()
driver.switch_to.frame(1)
driver.switch_to.default_content()
|
切换iframe的方法为:driver.switch_to.frame(),frame()中参数可以为id,name或者index,也可以为iframe元素
1 2 3 4 5 6 7
| driver.get('https://sahitest.com/demo/iframesTest.htm') driver.find_element(By.ID,'checkRecord').clear() driver.find_element(By.ID,'checkRecord').send_keys("6666") time.sleep(2)
driver.switch_to.frame(0) driver.find_element(By.ID, 'open-self').click()
|
编写本地代码,实现frame带id和name。
复制 sahitest 的iframe源码,src更改为哗哩哗哩网址。设置第一个iframe,id为iframe1,name为iframe_name
1 2 3 4 5 6 7 8 9 10
| driver.get("file:///Users/tester/Documents/web自动化测试/WebAutoTest/HTML/iframe_1.html") driver.find_element(By.ID,"checkRecord").clear() driver.find_element(By.ID, "checkRecord").send_keys("666") time.sleep(3)
driver.find_element(By.ID,'open-self').click()
driver.switch_to.frame("iframe_name") driver.find_element(By.XPATH, '//span[text()="番剧"]').click()
|
最后是根据定位iframe元素进行切换
1 2 3 4 5 6 7 8 9
| driver.get('https://sahitest.com/demo/iframesTest.htm') driver.find_element(By.ID,'checkRecord').clear() driver.find_element(By.ID,'checkRecord').send_keys("6666") time.sleep(2)
ele = driver.find_element(By.CSS_SELECTOR, 'div#another iframe') driver.switch_to.frame(ele) driver.find_element(By.ID, 'open-self').click() time.sleep(2)
|
1 2 3 4 5 6 7
| ele = driver.find_element(By.CSS_SELECTOR, "body > iframe") driver.switch_to.frame(ele)
driver.switch_to.parent_frame()
driver.switch_to.default_content()
|
级联日期选择器
1 2 3
| driver.find_element(By.XPATH,'//input[@class="ivu-input ivu-input-default"]').click() driver.find_element(By.XPATH,'//Li[contains(text(),"北京")]').click() driver.find_element(By.XPATH,'//Li[contains(text(),"王府井")]').click()
|
文件
上传
不推荐此方法—-绝对路径(使用较少):
1 2 3 4
| upload = driver.find_element(By.ID, 'file') upload.send_keys(r"/Users/tester/Documents/web自动化测试/WebAutoTest/file/Logo.jpg") driver.find_element(By.NAME, 'submit').click()
|
推荐此方法—-相对路径(使用较多):
项目目录下建立—-utils文件包:
1 2 3 4
| import os def get_logo_path(): path = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))),"file") return path
|
1 2 3 4 5 6 7 8
| from utils.get_filepath import get_logo_path
path = get_logo_path()
upload = driver.find_element(By.ID, 'file') upload.send_keys(r"{}".format(path)) driver.find_element(By.NAME, 'submit').click()
|
下载
项目目录下建立—-utils文件包:
1 2 3 4
| import os def download_file_path(): path = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))),"file") return path
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| from utils.get_filepath import download_file_path
path = download_file_path() + "/LATEST_RELEASE" if os.path.exists(path): print("文件存在") os.remove(path) print("文件已删除")
chromeOptions = webdriver.Chromeoptions() prefs = {"download.default_directory": "{}".format(download_file_path())} chromeOptions.add_experimental_option("prefs", prefs) driver = webdriver.Chrome(chromeOptions)
driver.find_element(By.XPATH, '/html/body/table/tbody/tr[156]/td[2]/a').click() time.sleep(3)
|
其他
验证码
一、验证码在自动化测试过程中如何处理
1、找开发屏蔽或测试跳过
2、找开发置为默认或万能码
3、使用cookie跳过,使用抓包工具或代码保存
1 2 3 4 5 6 7 8
| driver.get("http://www.xxxx.cn/")
driver.add_cookie({'name':'Login_UserNumber', 'value':'username'}) driver.add_cookie({'name':'Login_Passwd', 'value':'password'})
driver.get("http://www.xxxx.cn/") time.sleep(3)
|
4、使用pytesserac和Pillow实现验证码识别
1 2 3 4 5
| 使用pytesseract模块和pillow模块解决安装 pytesseract模块 pip3 install pytesseract 安装Pillow pip3 install pillow
|
2.网站验证码解决思路
截屏整个页面
获得验证码坐标数据
根据坐标数据抠图
使用pytesseract模块进行验证
可以使用pytesseract 模块和 PIL 模块解决不太复杂的验证码问题,实现步骤如下:
- 安装pytesseract模块 pip install pytesseract
- 安装 PIL 模块 pip install pil
使用第三方的API来实现
对于复杂的验证码,我们可以使用第三方的API来实现,本节我们将使用一个名为:万维易源的APl来解决验证码问题,网址是:https://www.showapi.com/
识别验证码的地址是:https://www.showapi.com/api/lookPoint
ddddocr是Python第三方库,识别度高达99%
1
| pip install -i https://pypi.tuna.tsinghua.edu.cn/simple ddddocr
|
等待
Selenium 三种等待方式在UI自动化测试中,必然会遇到环境不稳定,网络慢的情况,这时如果不做任何处理的话,代码会由于没有找到元素而报错。另外,一种情况就是页面使用ajax 异步加载机制。这时我们就要用到wait,而在 Selenium 中,我们可以用到一共三种等待,每一种等待都有自已的优点或缺点。
time.sleep(固定等待)
在开发自动化框架过程中,最忌讳使用python 自带模块的time 的sleep 方式进行等待,虽然可以自定义等待时间,但当网络条件良好时,依旧按照预设定的时间继续等待,导致整个项目的自动化时间无限延长,不建议使用。
(注:脚本调试过程时,还是可以使用的,方便快捷)
implicitly_wait(隐式等待)
隐式等待实际是设置了一个最长等待时间,如果在规定时间内网页加载完成,则执行下一步,否则一直等到时间结束,然后执行下一步。这样的隐式等待会有个坑,我们都知道 JavaScript一般都是放在我们的 body的最后进行加载,实际这是页面上的元素都已经加载完毕,我们却还在等待全部页面加载结束。
(注:隐式等待对整个driver周期都起作用,在最开始设置一次就可以了。不要当作固定等待使用,到哪都来一下隐式等待。)
1 2
| def test_wait(self): self.drive.implicitly_wait(10)
|
WebDriverWait(显式等待)(推荐)
WebDriverWait是selenium提供得到显示等待模块引入路径:
1
| from selenium.webdriver.support.wait import WebDriverWait
|
WebDriverWait参数
# |
参数 |
参数说明 |
1 |
driver |
传入WebDriver实例 |
2 |
timeout |
超时时间,等待的最长时间 |
3 |
poll_frequency |
调用until或unti_not中的方法的间隔时间,默认是o.5秒 |
4 |
ignored_exceptions |
忽略的异常 |
这个模块中,一共只有两种方法 until与until_not。
# |
参数 |
参数说明 |
1 |
method |
在等待期间,每隔一段时间调用这个传入的方法,直到返回值不是False |
2 |
message |
如果超时,抛出TimeoutException,将message传入异常 |
1 2 3 4 5 6 7 8 9
| from selenium.webdriver.support import expected_conditions as EC
def test_wait(self): # WebDriverWait(self.drive,2) wait = WebDriverWait(self.drive,2) wait.until(EC.title_is('百度一下')) if __name__ = '__main__': case.test_wait()
|
鼠标键盘
Selenium中的鼠标和键盘事件被封装在ActionChains类中,正确的使用方法是:ActionChains(driver).click(btn).perform()
下面列出ActionChains中常用方法:
# |
方法 |
方法描述 |
1 |
click(on_element=None) |
单击鼠标左键 |
2 |
click_and_hold(on_element=None) |
点击鼠标左键,不松开 |
3 |
context_click(on_element=None) |
点击鼠标右键 |
4 |
double_click(on_element=None) |
双击鼠标左键 |
5 |
drag_and_drop(source,target) |
拖拽到某个元素然后松开 |
6 |
drag_and_drop_by_offset(source,xoffset,yoffset) |
拖拽到某个坐标然后松开 |
7 |
key_down(value,element=None) |
按下某个键盘上的键 |
8 |
key_up(value,element=None) |
松开某个键 |
9 |
move_by_offset(xoffset,yoffset) |
鼠标从当前位置移动到某个坐标 |
10 |
move_to_element(to_element) |
鼠标移动到某个元素 |
11 |
move_to_element_with_offset(to_element,xoffset, yoffset) |
移动到距某个元素(左上角坐标)多离的位置 |
12 |
perform() |
执行链中的所有动作 |
13 |
release(on_element=None) |
在某个元素位置松开鼠标左键 |
14 |
send_keys(*keys_to_send) |
发送某个键到当前焦点的元素 |
15 |
send_keys_to_element(element,*keys_to_send) |
发送某个键到指定元素 |
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| btn = driver.find_element_by_xpath("/html/body/form/input[3]")
ActionChains(driver).click(on_element=btn).perform() btn = driver.find_element_by_xpath("/html/body/form/input[2]")
ActionChains(driver).double_click(btn).perform() btn = driver.find_element_by_xpath("/html/body/form/input[4]")
ActionChains(driver).context_click(btn).perform()
ele4 = driver.find_element_by_css_selector("//*[@id='conar']/div/div/div/table/tbody[1]/tr[1]/td[1]/span/a") target = driver.find_element_by_css_selector('#kw') ActionChains(driver).drag_and_drop(ele4,target).perform()
ele5 = driver.find_element_by_css_selector('#kw') ActionChains(driver).click_and_hold(ele5).release().perform()
|
键盘事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| kw = driver.find_element_by_id("kw") kw.send_keys("selenium")
kw.send_keys(Keys.CONTROL, "a")
kw.send_keys(Keys.CONTROL, "c")
kw.send_keys(Keys.CONTROL, "x")
kw.send_keys(Keys.CONTROL, "v")
kw.send_keys(Keys.DELETE)
kw.send_keys(Keys.ENTER)
kw.send_keys(Keys.BACKSPACE)
kw.send_keys(Keys.TAB)
kw.send_keys(Keys.F5)
|
滚动条
Selenium 执行 JavaScript 脚本
在web自动化,经常会遇到页面显示内容太多的时候,页面就会出现滚动条,一般有两种方式进行下拉,一种是直接下拉到底部/顶部/中部,或者直接给定元素,直接下拉到指定元素的位置。
WebDriver有两个方法来执行JavaScript,分别是:
通过 JavaScript 通常可以实现页面滚动,下面通过实例来演示一下他们的用法:
两种方式,都是通过selenium框架,执行js代码的方式来实现,只是传递的js代码不一样:
1
| self.driver.execute_script(js)
|
第一种:自定义直接下拉到顶部或底部,根据需要进行选择
滚动到底部:
1 2
| window.scrollTo(0, document.body.scrollHeight); self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
|
滚动到页面中间:
1
| pywindow.scrollTo(0, document.body.scrollHeight/2);
|
滚动到页面顶部:
1 2
| window.scrollTo(0, document.body.scrollHeight/document.body.scrollHeight); self.driver.execute_script("window.scrollTo(0, 0);")
|
指定位置的下滑或上滑:
1 2 3 4 5 6 7 8 9
| self.driver.execute_script("window.scrollBy(0, 500);")
self.driver.execute_script("window.scrollBy(0, -500);")
target_element = self.driver.find_element_by_id("target-element-id") self.driver.execute_script("arguments[0].scrollIntoView();", target_element)
|
实现逐渐下滑而没有停顿的效果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import time
def scroll_to_bottom(driver): page_height = driver.execute_script("return document.body.scrollHeight") def scroll(): current_scroll = driver.execute_script("return window.pageYOffset") next_scroll = current_scroll + 10 if next_scroll < page_height: driver.execute_script(f"window.scrollTo(0, {next_scroll});") driver.execute_script("window.requestAnimationFrame(arguments[0]);", scroll) driver.execute_script("window.requestAnimationFrame(arguments[0]);", scroll) time.sleep(2)
|
1 2 3 4 5 6 7 8
| def test2(self): js='return document.title' title = self.driver.execute_script(js) print(title) def test3(self): js='var q = document.getElementById("kw");q.style.border="2px solid red" self.driver.execute_script(js)
|
1 2 3 4 5 6 7 8 9
| def wait_for_window(self, timeout=2): time.sleep(round(timeout / 1000)) wh_now = self.driver.window_handles wh_then = self.vars["window_handles"] if len(wh_now) > len(wh_then): return set(wh_now).difference(set(wh_then)).pop()
self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
|
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| def scroll_to(self, type): ''' 移动浏览器的滚动条,滚动屏幕到百分比的位置 目前分为移动到末尾、移动到中间、移动到顶部。以及每次移动1/5,这种只能使用于浏览器的滚动条不在body里面,若有修改过滚动条的属性,无法进行下滑 :param type: :return: ''' if type == 'end': js = "window.scrollTo(0, document.body.scrollHeight);" elif type == 'middle': js = "window.scrollTo(0, document.body.scrollHeight/2);" elif type == 'top': js = "window.scrollTo(0, document.body.scrollHeight/document.body.scrollHeight);" else: js = "window.scrollTo(0, document.body.scrollHeight/5);" try: self.driver.execute_script(js) except Exception as e: mylog.exception("scroll_to_移动屏幕到{0}失败,请检查{1}".format(type, e)) self.save_imgs(model=type) raise
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| from selenium import webdriver from selenium.webdriver.common.action_chains import ActionChains
driver = webdriver.Chrome()
driver.get("https://example.com")
target_element = driver.find_element_by_css_selector("#target-element")
actions = ActionChains(driver)
actions.move_to_element(target_element).perform()
driver.quit()
|
屏幕截屏
Selenium屏幕截图
WebDriver内置了一些在测试中捕获屏幕并保存的方法:
# |
方法 |
方法描述 |
1 |
save_screenshot(filename) |
获取当前屏幕截图并保存为指定文件,filename指指定保存的路径或者图片的文件名 |
2 |
get_screenshot_as_base64() |
获取当前屏幕截图base65编码字符串 |
3 |
get_screenshot_as_file(fimename) |
获取当前的屏幕截图,使用完整的路径 |
4 |
get_screenshot_as_png() |
获取当前屏幕截图的二进制文件数据 |
全屏截图:
1 2 3 4 5 6 7 8 9 10 11
| driver.save_screenshot('screenshot.png')
driver.get_screenshot_as_file("screenshot.png")
st=strftime("%Y-%m-%d-%H-%M-%S",localtime(time())) fime_name = st+'.png' self.driver.save_screenshot(fime_name)
path = os.path.abspath('scrennshot') file_path = path+'/'+fime_name self.driver.get_screenshot_as_file(file_path)
|
获取当前屏幕截图base65编码字符串:
1 2
| screenshot_base64 = driver.get_screenshot_as_base64()
|
获取当前屏幕截图的二进制文件数据:
1 2
| screenshot_data = driver.get_screenshot_as_png()
|
屏幕截图示例(代码肯定会报错的哈,这里是为了演示测试失败时,自动截图的功能(比如有的时候弹出不应该有的警告或者错误信息什么的,就可以截图保留记录))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| from selenium import webdriver import unittest from selenium.common.exceptions import NoSuchElementException class Login(unittest.TestCase): @classmethod def setUpClass(cls): cls.driver = webdriver.Chrome() cls.driver.implicitly_wait(5) cls.driver.maximize_window() cls.driver.get("https://www.cnblogs.com/") def test_login(self): try: login_area = self.driver.find_element_by_css_selector('#login_area') register = login_area.find_element_by_link_text('注册111') register.click() except NoSuchElementException: self.driver.save_screenshot('image/test_login/test_login_area.png') raise @classmethod def tearDownClass(cls): cls.driver.quit()
|
PyAutoGUI
PyAutoGUI
是一种跨平台的GUI自动化工具,它接管了鼠标、键盘使用权,基本上是完全仿照人的操作。
PyAutoGUI的主要功能
- 鼠标操作:PyAutoGUI可以模拟鼠标移动、点击、拖拽等操作,可以控制鼠标的位置和点击的坐标。
- 键盘操作:PyAutoGUI可以模拟键盘按键和组合键的操作,如按下和释放按键、输入文本等。
- 屏幕操作:PyAutoGUI可以获取屏幕的大小、截屏、查找指定图像的位置等。
- 延时控制:PyAutoGUI可以控制鼠标和键盘操作的延时,以确保操作的正确性和稳定性。
1.pyautogui
:官方文档
1
| https://pyautogui.readthedocs.org/
|
2.安装安装pyautogui
1 2 3 4
| pip install pyautogui python.exe -m pip install --upgrade pip
|
GUI 控制功能
控制鼠标键盘使用的模块为:pyautogui,这个模块操作起鼠标键盘的时候,非常的迅速,而且如果该模块控制了鼠标后,程序比较难关闭,这时我们有两个方法专门针对以上的情况:
自动防故障功能
1
| pyautogui.FAILSAFE =False
|
默认这项功能为True, 这项功能意味着:当鼠标的指针在屏幕的最坐上方,程序会报错;目的是为了防止程序无法停止;
停顿功能
意味着所有pyautogui的指令都要暂停一秒;其他指令不会停顿;这样做,可以防止键盘鼠标操作太快;
鼠标操作
控制鼠标移动
获得屏幕分辨率
1 2 3 4 5 6
|
print(pyautogui.size()) width,height = pyautogui.size() print(width,height)
|
移动鼠标
1 2 3 4 5
| pyautogui.moveTo(100,300,duration=1)
pyautogui.moveRel(100,500,duration=1)
|
获取鼠标位置
1 2
| print(pyautogui.position())
|
控制鼠标点击
1 2 3 4 5 6 7 8 9 10 11 12
| pyautogui.click(10,10) pyautogui.click(10,10,button='left') pyautogui.click(1000,300,button='right') pyautogui.click(1000,300,button='middle')
pyautogui.doubleClick(10,10) pyautogui.rightClick(10,10) pyautogui.middleClick(10,10)
pyautogui.mouseDown() pyautogui.mouseUp()
|
控制鼠标拖动
1 2
| pyautogui.dragTo(100,300,duration=1)
|
将鼠标拖动到指定的坐标;duration 的作用是设置移动时间,所有的gui函数都有这个参数,而且都是可选参数;
1 2 3
|
pyautogui.dragRel(100,500,duration=4)
|
控制鼠标滚动
控制鼠标滚动的函数是scroll(), 传入一个整数的参数,说明向上或向下滚动多少个单位;单位根据操作系统不同而不同;
屏幕处理
获取屏幕截图
我们控制鼠标的操作,不能盲目的进行,所以我们需要监控屏幕上的内容,从而决定要不要进行对应的操作, pyautogui 提供了一个方法screenshot(),可以返回一个Pillow的image对象;
这里有三个常用函数:
- im = pyautogui.screenshot():返回屏幕的截图,是一个Pillow的image对象
- im.getpixel((500, 500)):返回im对象上,(500,500)这一点像素的颜色,是一个RGB元组
- pyautogui.pixelMatchesColor(500,500,(12,120,400)) :是一个对比函数,对比的是屏幕上(500,500)这一点像素的颜色,与所给的元素是否相同;
1 2 3
| im = pyautogui.screenshot() im.save('屏幕截图.png')
|
识别图像
首先,我们需要先获得一个屏幕快照,例如我们想要点赞,我们就先把大拇指的图片保存下来;然后使用函数:locateOnScreen(‘zan.png’) ,如果可以找到图片,则返回图片的位置,如:Box(left=25, top=703, width=22, height=22);如果找不到图片,则返回None;如果,屏幕上有多处图片可以匹配,则需要使用locateAllOnScreen(‘zan.png’) ,如果匹配到多个值,则返回一个list,参考如下:
1 2 3 4 5 6 7 8 9 10
| import pyautogui pyautogui.PAUSE = 1
btm = pyautogui.locateOnScreen('zan.png') print(btm)
btm = pyautogui.locateAllOnScreen('zan.png') print(list(btm))
|
pyautogui.center((left, top, width, height)) 返回指定位置的中心点;这样,我们就可以再配合鼠标操作点击找到图片的中心;
键盘输入
键盘输入函数
- pyautogui.keyDown() : 模拟按键按下;
- pyautogui.keyUp() : 模拟按键释放;
- pyautogui.press() : # 就是调用keyDown() & keyUp(),模拟一次按键
- pyautogui.typewrite(‘this’,0.5) : 第一参数是输入内容,第二个参数是每个字符间的间隔时间;
- pyautogui.typewrite([‘T’,‘h’,‘i’,‘s’]):typewrite 还可以传入单字母的列表;
1 2 3 4 5 6
| 输出: $ ; pyautogui.keyDown('shift') pyautogui.press('4') pyautogui.keyUp('shift')
pyautogui.typewrite('$$$$', 0.5)
|
键盘特殊按键
有时我们需要输入一些特殊的按键,比如向左的箭头,这些有相对应的键盘字符串表示,例如:
1 2
| pyautogui.typewrite(['T','i','s','left','left','h',])
|
解释:这里的left就是向左的箭头;诸如此类的键盘字符串,还有很多,参考下表:
键盘字符串 |
说明 |
enter(或return 或 \n) |
回车 |
esc |
ESC键 |
shiftleft, shiftright |
左右SHIFT键 |
altleft, altright |
左右ALT键 |
ctrlleft, ctrlright |
左右CTRL键 |
tab (\t) |
TAB键 |
backspace, delete |
BACKSPACE 、DELETE键 |
pageup, pagedown |
PAGE UP 和 PAGE DOWN键 |
home, end |
HOME 和 END键 |
up, down, left,right |
箭头键 |
f1, f2, f3…. f12 |
F1…….F12键 |
volumemute, volumedown,volumeup |
声音变大变小静音(有些键盘没有) |
pause |
PAUSE键,暂停键 |
capslock |
CAPS LOCK 键 |
numlock |
NUM LOCK 键 |
scrolllock |
SCROLLLOCK 键 |
insert |
INSERT键 |
printscreen |
PRINT SCREEN键 |
winleft, winright |
Win键(windows ) |
command |
command键(Mac OS X ) |
option |
option(Mac OS X) |
快捷键
如果我们需要模拟复制的快捷键 ctrl + c ,如果用前面的方法,则代码为:
1 2 3 4 5
| pyautogui.keyDown('ctrl') pyautogui.keyDown('c') pyautogui.keyUp('c') pyautogui.keyUp('ctrl') 1234
|
快捷键的按键与释放顺序非常关键,这时我们可以使用 pyautogui.hotkey(),这个函数可以接受多个参数,按传入顺序按下,再按照相反顺序释放。上述快捷键 ctrl + c ,可以将代码变为:
1 2
| pyautogui.hotkey('ctrl','c') 1
|
提示信息框
提示框/警告框
1 2 3
| import pyautogui a = pyautogui.alert(text='This is an alert box.', title='Test') print(a)
|
例子:
项目
练习一
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
|
import time import allure from selenium import webdriver from selenium.webdriver.common.by import By
def ui_exception_record(func): def inner(*args, **kwargs): try: return func(*args, **kwargs) except Exception: driver = args[0].driver timestamp = int(time.time()) image_path = f"./image/image_{timestamp}.PNG" page_source_path = f"./page_source/page_source_{timestamp}.html " driver.save_screenshot(image_path) with open(page_source_path, "w", encoding="utf-8") as f: f.write( driver.page_source) allure.attach.file(image_path, name="picture", attachment_type=allure.attachment_type.PNG) allure.attach.file(page_source_path, name="pagesource", attachment_type=allure.attachment_type.TEXT) raise Exception return inner
class TestBaidu: def setup(self): self.driver = webdriver.Chrome()
def teardown(self): self.driver.quit()
@ui_exception_record def test_baidu(self): self.driver.get("https://www.baidu.com/") self.driver.find_element(By.ID, "su").click()
|
练习二
Jpress项目
- testcases 测试用例
- data 测试数据
- logs log 日志
- config 配置文件
- reports 测试报告
- screenshots 截屏
- lib 第三方库
- util 类