1. 简介
本来上一篇就是练习篇的最后一篇文章了, 但是有的小伙伴私下反映说是做了那么多练习, 没有一个比较综合的 demo 练练手. 因此宏哥在这里又补存了一些常见的知识点进行练习, 在文章最后也通过实例给小伙伴们或者童鞋们进行了一个登录模块的自动化测试的实例, 其他的你可以照猫画虎地轻松的搞定和实现.
2. python-web 自动化 - 三种等待方式
当有元素定位不到时, 比如下拉框, 弹出框等各种定位不到时;
一般是两种问题: 1 , 有 frame ;2, 没有加等待
下面学习三种等待方式:
2.1 强制等待 sleep(xx)或者 Time.sleep
是在程序运行过程中使用 time 模块中的 sleep 进行代码的休眠进行强制等待, 是显式等待中的一种极端情况.
这种方法简单粗暴, 不管浏览器是否加载完, 程序都要等待规定的 xx 时间, 时间到了才继续执行下面的代码.
不建议总是用这种等待方式, 会严重影响程序的执行速度. 通过 time 模块中 sleep 进行代码的暂停, 但是实际使用过程中, 如果都以 sleep 进行控制严重影响了程序的运行.
2.1.1 参考代码 1
- # coding=utf-8
- # 1. 先设置编码, utf-8 可支持中英文, 如上, 一般放在第一行
- # 2. 注释: 包括记录创建时间, 创建人, 项目名称.
- '''
- Created on 2019-12-10
- @author: 北京 - 宏哥 QQ 交流群: 705269076
- Project: 《手把手教你》系列练习篇之 9-python+ selenium 自动化测试
- '''
- # 3. 导入模块
- import time
- # 强制等待 -- 代码休眠
- from selenium import webdriver
- driver = webdriver.Chrome()
- driver.get("https://www.baidu.com")
- time.sleep(3)
- driver.quit()
2.1.2 参考代码 2
- # coding=utf-8
- # 1. 先设置编码, utf-8 可支持中英文, 如上, 一般放在第一行
- # 2. 注释: 包括记录创建时间, 创建人, 项目名称.
- '''
- Created on 2019-12-10
- @author: 北京 - 宏哥 QQ 交流群: 705269076
- Project: 《手把手教你》系列练习篇之 9-python+ selenium 自动化测试
- '''
- # 3. 导入模块
- from selenium import webdriver
- from time import sleep
- driver = webdriver.Chrome()
- driver.get('https://www.baidu.com/')
- sleep(3)# 强制性等待 3s 再执行以下代码
- print(driver.current_url)
- driver.quit()# 退出驱动, 关闭所有窗口
2.2 隐性等待 implicitly_wait(xx)
这种方法是设置一个最长的等待时间, 如果在规定时间内网页全部元素加载完成, 则执行下一步, 否则一直等待时间截止才执行下一步. 比强制性等待智能些
! 隐性等待对整个 driver 的周期都起作用, 所以只要设置一次即可
2.2.1 参考代码 1
- # coding=utf-8
- # 1. 先设置编码, utf-8 可支持中英文, 如上, 一般放在第一行
- # 2. 注释: 包括记录创建时间, 创建人, 项目名称.
- '''
- Created on 2019-12-10
- @author: 北京 - 宏哥 QQ 交流群: 705269076
- Project: 《手把手教你》系列练习篇之 9-python+ selenium 自动化测试
- '''
- # 3. 导入模块
- from selenium import webdriver
- from selenium.common.exceptions import NoSuchElementException
- from time import ctime
- driver = webdriver.Firefox()
- # 设置隐式等待为 10 秒
- driver.implicitly_wait(10)
- driver.get("http://www.baidu.com")
- try:
- print(ctime())
- driver.find_element_by_id("kw22").send_keys('selenium')
- except NoSuchElementException as e:
- print(e)
- finally:
- print(ctime())
- driver.quit()
这里可以看到在 10 秒内没有找到想要找到的元素, 但是依旧执行了 10 秒, 然后报错, 如果修改代码为可以找到, 代码执行非常迅速.
implicitly_wait()默认参数的单位为秒, 本例中设置等待时长为 10 秒. 首先这 10 秒并非一个固定的等待时间, 它并不影响脚本的执行速度. 其次, 它并不针对页面上的某一元素进行等待. 当脚本执行到某个元素定位时, 如果元素可以定位, 则继续执行; 如果元素定位
不到, 则它将以轮询的方式不断地判断元素是否被定位到. 假设在第 6 秒定位到了元素则继续执行, 若直到超出设置时长 (10 秒) 还没有定位到元素, 则抛出异常.
2.2.2 参考代码 2
- # coding=utf-8
- # 1. 先设置编码, utf-8 可支持中英文, 如上, 一般放在第一行
- # 2. 注释: 包括记录创建时间, 创建人, 项目名称.
- '''
- Created on 2019-12-10
- @author: 北京 - 宏哥 QQ 交流群: 705269076
- Project: 《手把手教你》系列练习篇之 9-python+ selenium 自动化测试
- '''
- # 3. 导入模块
- from selenium import webdriver
- driver = webdriver.Chrome()
- driver.get('https://www.baidu.com/')
- driver.implicitly_wait(30)# 隐性等待, 最长 30s
- print(driver.current_url)
- driver.quit()
2.3. 显性等待 WebDriverWait
WebDriverWait 配合该类的 until()和 until_not()方法, 根据条件灵活的等待
程序每隔 xx 秒看一眼, 如果条件成立了, 则执行下一步, 否则继续等待, 直到超过设置的最长时间, 然后抛出 TimeoutException.
显式等待是你在代码中定义等待一定条件发生后再进一步执行你的代码.
A. 使用前, 先引用相关库
B. 确定元素的定位表达式
C. 使用 expected_conditions 对应的方法来生成判断条件
- WebDriverWait(driver,10,1).until(EC.visibility_of_element_located((By.ID,ele_locator)))
- WebDriverWait(driver,10,1).until(EC.visibility_of_element_located((By.XPATH,ele_locator)))
D. 调用 WebDriverWait 类设置等待总时长, 轮询周期
2.3.1 参考代码 1
- # coding=utf-8
- # 1. 先设置编码, utf-8 可支持中英文, 如上, 一般放在第一行
- # 2. 注释: 包括记录创建时间, 创建人, 项目名称.
- '''
- Created on 2019-12-10
- @author: 北京 - 宏哥 QQ 交流群: 705269076
- Project: 《手把手教你》系列练习篇之 9-python+ selenium 自动化测试
- '''
- # 3. 导入模块
- #A. 使用前, 先引用相关库
- from selenium import webdriver
- from selenium.webdriver.support.wait import WebDriverWait
- from selenium.webdriver.support import expected_conditions as EC
- from selenium.webdriver.common.by import By
- driver = webdriver.Chrome()# 打开 Chrome 浏览器
- driver.get('https://www.baidu.com/')# 打开百度
- driver.find_element_by_xpath('//div[@id="u1"]//a[@name="tj_login"]').click()# 点击[登录] ;click() 方法, 可模拟在按钮上的一次鼠标单击.
- # B. 确定元素的定位表达式
- ele_locator = "TANGRAM__PSP_10__footerULoginBtn"# 通过 id, 确定'用户名登录'元素
- # C. 使用 expected_conditions 对应的方法来生成判断条件
- # EC. 方法名(定位方式, 定位表达式)
- # EC.visibility_of_element_located(By.ID,ele_locator)# 元素可见
- # D. 调用 WebDriverWait 类设置等待总时长, 轮询周期
- # WebDriverWait(driver, 超时时长, 调用频率(默认 0.5s)).until(可执行方法, 超时时返回的信息)
- # 等待 10 秒钟, 每隔 1 秒去查看对应的元素是否可见; 如果可见, 继续下一步操作; 如果不可见, 则继续等待, 直到 10s 结束, 如果元素还是不可见, 则抛出超时异常
- WebDriverWait(driver,10,1).until(EC.visibility_of_element_located((By.ID,ele_locator)))
- driver.find_element_by_id('TANGRAM__PSP_10__footerULoginBtn').click()# 点击[用户名登录]
- driver.close()# 关闭当前窗口
2.3.2 参考代码 2
- # coding=utf-8
- # 1. 先设置编码, utf-8 可支持中英文, 如上, 一般放在第一行
- # 2. 注释: 包括记录创建时间, 创建人, 项目名称.
- '''
- Created on 2019-12-10
- @author: 北京 - 宏哥 QQ 交流群: 705269076
- Project: 《手把手教你》系列练习篇之 9-python+ selenium 自动化测试
- '''
- # 3. 导入模块
- from time import ctime
- from selenium import webdriver
- from selenium.webdriver.common.by import By
- from selenium.webdriver.support.ui import WebDriverWait
- from selenium.webdriver.support import expected_conditions as EC
- dr = webdriver.Chrome()
- dr.get('https://www.baidu.com')
- try:
- print(ctime())
- element = WebDriverWait(dr, 10).until(
- EC.presence_of_element_located((By.ID, "kw"))
- )
- # WebDriverWait(driver=self.driver, timeout=300, poll_frequency=0.5, ignored_exceptions=None)
- # driver: 浏览器驱动
- # timeout: 最长超时等待时间
- # poll_frequency: 检测的时间间隔, 默认为 500ms
- # ignore_exception: 超时后抛出的异常信息, 默认情况下抛 NoSuchElementException 异常
- print("我已找到")
- finally:
- print(ctime())
- dr.quit()
执行结果:
以上代码执行后就发现, 整段代码执行速度非常快, 即使我在 WebDriverWait 中设置 10 秒, 也不会等待 10 秒的情况, 因为在不到一秒内, 已经完成了加载并定位 id 为 "kw" 的元素.
通过 WebDriverWait 和 ExpectedCondition 组合使用, 让我们的代码执行只需要等待需要的时长, 而不是固定的时长, 这样最大限度的节省时间.
此外 ExpectedCondition 类中提供了很多预期条件判断方法, 省去了再创建包的功夫:
"""
title_is: 判断当前页面的 title 是否等于预期
title_contains: 判断当前页面的 title 是否包含预期字符串
presence_of_element_located: 判断某个元素是否被加到了 dom 树里, 并不代表该元素一定可见
visibility_of_element_located: 判断某个元素是否可见. 可见代表元素非隐藏, 并且元素的宽和高都不等于 0
visibility_of: 跟上面的方法做一样的事情, 只是上面的方法要传入 locator, 这个方法直接传定位到的 element 就好了
presence_of_all_elements_located: 判断是否至少有 1 个元素存在于 dom 树中. 举个例子, 如果页面上有 n 个元素的 class 都是'column-md-3', 那么只要有 1 个元素存在, 这个方法就返回 True
text_to_be_present_in_element: 判断某个元素中的 text 是否 包含 了预期的字符串
text_to_be_present_in_element_value: 判断某个元素中的 value 属性是否包含了预期的字符串
frame_to_be_available_and_switch_to_it: 判断该 frame 是否可以 switch 进去, 如果可以的话, 返回 True 并且 switch 进去, 否则返回 False
invisibility_of_element_located: 判断某个元素中是否不存在于 dom 树或不可见
element_to_be_clickable - it is Displayed and Enabled: 判断某个元素中是否可见并且是 enable 的, 这样的话才叫 clickable
staleness_of: 等某个元素从 dom 树中移除, 注意, 这个方法也是返回 True 或 False
element_to_be_selected: 判断某个元素是否被选中了, 一般用在下拉列表
element_located_to_be_selected
element_selection_state_to_be: 判断某个元素的选中状态是否符合预期
element_located_selection_state_to_be: 跟上面的方法作用一样, 只是上面的方法传入定位到的 element, 而这个方法传入 locator
alert_is_present: 判断页面上是否存在 alert
"""
另外这里使用了 until()函数也可以使用 until_not()
until_not(method, message='')
调用该方法体提供的回调函数作为一个参数, 直到返回值为 False
until(method, message='')
调用该方法体提供的回调函数作为一个参数, 直到返回值为 True
3. 下拉框 or 弹框
通常情况下, 在网页中会有一些下拉框进行选择或者给你一个弹框告诉你需要进行确认, 遇到这样的情况如何解决呢, 宏哥在这里就来简单地说一说......''
3.1 代码实现:
3.2 参考代码:
- # coding=utf-8
- # 1. 先设置编码, utf-8 可支持中英文, 如上, 一般放在第一行
- # 2. 注释: 包括记录创建时间, 创建人, 项目名称.
- '''
- Created on 2019-12-09
- @author: 北京 - 宏哥 QQ 交流群: 705269076
- Project: python+ selenium 自动化测试练习篇 8
- '''
- # 3. 导入模块
- import time
- from selenium import webdriver
- from selenium.webdriver.common.by import By
- from selenium.webdriver.common.keys import Keys
- from selenium.webdriver.common.action_chains import ActionChains
- from selenium.webdriver.support.select import Select
- driver = webdriver.Chrome()
- driver.get("https://www.baidu.com")
- time.sleep(5)
- # driver.find_element(By.ID, "kw").send_keys("selenium&python")
- # driver.find_element(By.ID, "su").click()
- # driver.find_element(By.XPATH, '/html/body/div[1]/div[5]/div[1]/div[2]/div[1]/div[2]/div[1]').click()
- # driver.find_element(By.XPATH, '/html/body/div[1]/div[5]/div[1]/div[2]/div[1]/div[1]/span[2]').click()
- # 折腾半天才发现定位出来的不是下拉框......
- driver.find_element(By.LINK_TEXT, '设置').click()
- driver.find_element(By.LINK_TEXT, '搜索设置').click()
- time.sleep(2)
- sel = driver.find_element(By.XPATH, '//*[@id="nr"]') # 定位下拉框
- Select(sel).select_by_value('50') # 通过 value 的值进行选定条目
- driver.find_element(By.CLASS_NAME, 'prefpanelgo').click()
- time.sleep(2)
- driver.switch_to.alert.accept() # 此处同意警告框提示内容
- time.sleep(2)
- driver.quit()
3.3 运行结果:
运行代码后, 控制台打印如下图的结果
3.4 浏览器
浏览器跳转到设置页面, 会提示 "已记录下您的偏好"
在百度首页想要进行设置每次搜索页面显示的数目, 就会有一个下拉框进行选择, 这里可以导入 Select 这个模块进行对下拉框进行选择, 通常下拉框的列表是包含一个值的, 这样就可以通过 value 来选定, 完成设置之后, 当然是要保存设置啦, 点击保存发现, 居然有
一个弹框, 这可如何是好, 切换至 alert 之后接受吧......
通过在编辑器中的提示, 我们同样可以给弹框发送一个值, 当然这里设置中并没有出现这类情况.
这次对下拉框和弹框的处理过程中, 折腾了不短时间, 原因就是元素等待的问题, 下回要仔细研究下元素等待的问题了.
4. 自动化测试 demo
使用 selenium 进行测试, 当然是需要进行验证的, 此时结合 python 的断言函数就变得非常有用了. 测试中设定好预期, 当实际情况与预期有差别, 给出错误信息, 好像正是所希望的.
来一段简单的代码看下断言的基本使用:
- # coding:utf-8
- import time
- from selenium import webdriver
- dr = webdriver.Chrome()
- dr.maximize_window()
- dr.get("https://www.baidu.com")
- print(dr.title)
- try:
- assert (dr.title == "百度, 你就知道")
- except:
- print('标题错误, 请查看错误信息')
- time.sleep(5)
- dr.quit()
获取页面的 title 是否符合我们的预期, 当打开的网页 title 不是我们想要的, 不仅从函数 assert 的提示信息看到原因, 也可输出简单的提示信息.
Web 登录测试是很常见的测试, 手动测试大家再熟悉不过了, 那如何进行自动化登录测试呢! 本文就基于 python+selenium 结合 unittest 单元测试框架来进行一次简单但比较完整的 cnblog 自动化登录测试, 可提供点参考! 下面就包括测试代码和每种测试情况的截图.
另一种方式是结合 python 测试框架 unittest 进行验证. 以下的代码只是为了演示:
- # coding=utf-8
- # 1. 先设置编码, utf-8 可支持中英文, 如上, 一般放在第一行
- # 2. 注释: 包括记录创建时间, 创建人, 项目名称.
- '''
- Created on 2019-12-10
- @author: 北京 - 宏哥 QQ 交流群: 705269076
- Project: python+ selenium 自动化测试练习篇 9
- '''
- # 3. 导入模块
- import unittest
- from selenium import webdriver
- from time import sleep
- '''
- cnblog 的登录测试, 分下面几种情况:
- (1)用户名, 密码正确
- (2)用户名正确, 密码不正确
- (3)用户名正确, 密码为空
- (4)用户名错误, 密码正确
- (5)用户名为空, 密码正确(还有用户名和密码均为空时与此情况是一样的, 这里就不单独测试了)
- '''
- class LoginCase(unittest.TestCase):
- def setUp(self):
- self.dr = webdriver.Chrome()
- self.dr.maximize_window()
- # 定义登录方法
- def login(self, username, password):
- self.dr.get('https://passport.cnblogs.com/user/signin') # cnblog 登录页面
- self.dr.find_element_by_id('LoginName').send_keys(username)
- self.dr.find_element_by_id('Password').send_keys(password)
- self.dr.find_element_by_id('submitBtn').click()
- def test_login_success(self):
- '''用户名, 密码正确'''
- self.login('北京 - 宏哥', '!qaz2wsx') # 正确用户名和密码
- sleep(3)
- link = self.dr.find_element_by_id('lnk_current_user')
- self.assertTrue('北京 - 宏哥' in link.text) # 用 assertTrue(x)方法来断言 bool(x) is True 登录成功后用户昵称在 lnk_current_user 里
- self.dr.get_screenshot_as_file("D:\\cnblogtest\\login_success.jpg") # 截图 可自定义截图后的保存位置和图片命名
- def test_login_pwd_error(self):
- '''用户名正确, 密码不正确'''
- self.login('北京 - 宏哥', 'kemi') # 正确用户名, 错误密码
- sleep(2)
- error_message = self.dr.find_element_by_class_name('ajax-error-box').text
- self.assertIn('用户名或密码错误', error_message) # 用 assertIn(a,b)方法来断言 a in b '用户名或密码错误'在 error_message 里
- self.dr.get_screenshot_as_file("D:\\cnblogtest\\login_pwd_error.jpg")
- def test_login_pwd_null(self):
- '''用户名正确, 密码为空'''
- self.login('北京 - 宏哥', '') # 密码为空
- error_message = self.dr.find_element_by_id('Password-error').text
- self.assertEqual(error_message, '请输入密码') # 用 assertEqual(a,b)方法来断言 a == b 请输入密码等于 error_message
- self.dr.get_screenshot_as_file("D:\\cnblogtest\\login_pwd_null.jpg")
- def test_login_user_error(self):
- '''用户名错误, 密码正确'''
- self.login('北京 - 宏哥 1', '!qaz2wsx') # 密码正确, 用户名错误
- sleep(2)
- error_message = self.dr.find_element_by_id('ajax-error-box').text
- self.assertIn('用户名或密码错误', error_message) # 用 assertIn(a,b)方法来断言 a in b
- self.dr.get_screenshot_as_file("D:\\cnblogtest\\login_user_error.jpg")
- def test_login_user_null(self):
- '''用户名为空, 密码正确'''
- self.login('','!qaz2wsx') # 用户名为空, 密码正确
- error_message = self.dr.find_element_by_id('LoginName-error').text
- self.assertEqual(error_message, '请输入登录用户名') # 用 assertEqual(a,b)方法来断言 a == b
- self.dr.get_screenshot_as_file("D:\\cnblogtest\\login_user_null.jpg")
- def tearDown(self):
- sleep(2)
- print('自动测试完毕!')
- self.dr.quit()
- if __name__ == '__main__':
- unittest.main()
通过以上代码, 我们可以看到使用 selenium 和 unittest 框架结合, 已经可以对用户登陆的模块做一个简单的自动化测试.
但是现在博客园的登陆机制的改变有可能部分用例不成功, 但是你知道怎么做就可以了.
测试截图如下:
正确用户名和密码登录成功!
用户名正确, 密码为空
用户名为空, 密码正确
5. 小结
好了, 今天的分享就到这里吧!!! 谢谢各位的耐心阅读.
您的肯定就是我进步的动力. 如果你感觉还不错, 就请鼓励一下吧! 记得点波 推荐 不要忘记哦!!!
来源: https://www.cnblogs.com/du-hong/p/12010213.html