web自动化测试的工具

现在主流的web测试工具我们常用的就是selenium的那一套工具包括

  • 浏览器一般选择chrome
  • 浏览器对应的driver(chromedriver)
  • Python
  • Selenium库

web自动化测试的环境安装

  • 浏览器安装 下载浏览器下一步按照就好了
  • chromedriver 下载 可以到 npmmirror.com/
  • https://registry.npmmirror.com/binary.html?path=chromedriver/ 低版本
  • ​ 高版本
  • 注:找到和自己浏览器版本适配的driver版本 注:浏览器点击右上角‘…’—>帮助—>关于Google Chrome—>可以看到浏览器的版本,根据当前浏览器的版本下载

Selenium提供了一个webdriver_manager库,可以帮助自动下载和更新Chrome浏览器的驱动程序chromedriver.exe。您可以按照以下步骤操作:

  1. 安装webdriver_manager库。可以在命令行或终端中运行以下命令进行安装:

  2. ```
    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

安装指定版本:

1
pip install selenium==版本号

卸载selenium

1
pip uninstall selenium

web自动化测试脚本编写的基本步骤

1
from selenium import webdriver

创建驱动浏览器对象并启动浏览器

1
2
3
浏览器驱动对象 = webdriver.Firefox()
浏览器驱动对象 = webdriver.Chrome()
浏览器驱动对象 = webdriver.Edge()

编写自动化执行步骤

打开网页:

浏览器驱动对象.get(“网页链接”)

浏览器窗口最大化:

1
driver.maxmimize()

关闭驱动对象

1
浏览器驱动对象.quit()

刷新窗口

1
driver.refresh() *# 刷新*

使用 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

八大元素

  1. id
  2. name
  3. class_name(使用元素的class属性)
  4. xpath(基于文件路径)
  5. css(元素选择器定位)
  6. tag_name(元素的标签名称
  7. link_text(定位超链接,a标签)
  8. 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/")
# 只获取class属性的第一个元素
# driver.find_element(By.CLASS_NAME,'nav-search-input').send_keys("2024新年快乐")
# driver.find_element(By.CLASS_NAME,'channel-link').click()
# 获取class属性的所有元素
# driver.find_elements(By.CLASS_NAME,'channel-link')[4].click()
# for ele in driver.find_elements(By.CLASS_NAME,'channel-link'):
# print(ele.text)
# 错误用法
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")

精确匹配超链接

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()

模糊匹配超链接

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')
# 最后一个子元素,last()
driver.find_element(By.XPATH, '//a[@class="mnav c-font-normal c-color-t"][last()]')
动态ID
https://element.eleme.cn/#/zh-CN/component/cascader
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
#1、相对路径+索引定位   //form/span/input
driver.find_element(By.XPATH,"//form/span/input").send_keys("输入的内容")

#2、相对路径+属性定位 //input[@autocomplete='off']
driver.find_element(By.XPATH,"//input[@autocomplete='off']").send_keys("输入的内容")

#3、相对路径+通配符定位 *复制xpath不是万能的,经常会报错
driver.find_element(By.XPATH,"//*[@autocomplete='off']").send_keys("输入的内容")
driver.find_element(By.XPATH,"//*[@*='off']").send_keys("输入的内容")

#4、相对路径+部分属性值定位 input[starts-with(@autocomplete,'of')]
driver.find_element( # auto属性以of开头:starts-with
By.XPATH,"//input[starts-with(@autocomplete,'of')]"
).send_keys("输入的内容")

driver.find_element( # auto属性从第二个字符开始截取,为ff的:substring
By.XPATH,"//input[substring(@autocomplete,2)='ff']"
).send_keys("输入的内容")

driver.find_element( # auto属性包含字符of:contains
By.XPATH,"//input[contains(@autocomplete,'of')]"
).send_keys("输入的内容")

#5、相对路径+文本定位
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. 元素选择器

  • 语法:element 如:input

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
#1.使用css id选择器 定位用户名 输入admin
driver.find_element(By.CSS_SELECTOR, "#userA").send_keys("admin")

#2.使用css 属性选择器 定位密码框 输入123456
driver.find_element(By.CSS_SELECTOR, "[name='passwordA']").send_keys("123456")

#3.使用css class选择器 定位电话号码 输入18611112222
driver.find_element(By.CSS_SELECTOR, ".telA").send_keys("18611112222")

#4.使用css 元素选择器 定位span标签获取文本值
span = driver.find_element(By.CSS_SELECTOR, "span").text
print("获取的span标签文本值为:",span)

#5.使用层级选择器 定位email 输入123@qq.com
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常用汇总

选择器 格式 示例 示例说明
标签选择器 html标签 input 选择所有元素
ID选择器 #id属性值 #kw 选择所有id=’kw’的元素
类选择器 .class属性值 .nav-search-input 选择所有class=’nav-search-input’的元素
属性选择器1 [属性名] [name=”wd”] 选择所有name等于”wd”的元素
组合选择器 标签加属性描述 input.s_ipt 选择所有class=’_ipt’的元素
父子关系 元素1>元素2 div>a 选择所有父级是
后代关系 元素1 元素2 div a 选择
中的所有元素
第一子元素 :first-child a:first-child 选择所有元素且该元素是其父级的第一个元素
最后一个元素 :last-child a:last-child 选择所有元素且该元素是其父级的最后一个元素
顺序选择器 :nth-child(n) a:nth-child(2) 选择所有元素且该元素是其父级的第二个子元素

●使用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')
# input+name
driver.find_element(By.CSS_SELECTOR, 'input[name="wd"]').send_keys("软件测试老白")
# input+class
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' ) # 根据class
driver.find_element(By.CSS_SELECTOR, 'div#s-top-left a') # 根据id
driver.find_element(By.CSS_SELECTOR, '#s-top-left a') # 简写
# 百度首页地图,以下2种方式皆可
driver.find_element(By.CSS_SELECTOR, '#s-top-left a:nth-child(3)')
driver.find_elements(By.CSS_SELECTOR, '#s-top-left a')[2]

# a:first-child 第一个标签
driver.find_element(By.CSS_SELECTOR, '#s-top-left a:first-child')
# a:last-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
# 1、登录
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, 'username').send_keys('XieChuang')
chrome.find_element(By.ID, 'password').send_keys('123456')
# chrome.find_element_by_class_name('login_btn fl').click() # 登录的类名是复合类名,不能同时使用,fl只是一个左对齐的作用
chrome.find_element(By.CLASS_NAME, 'login_btn').click()
# 2、点击'进入商城购物'
# 登录成功后不是立马进行页面的,所以此处添加一个时间等待
time.sleep(3)
# 第三种元素定位方式,linktext
chrome.find_element(By.LINK_TEXT, '进入商城购物').click()
# 3、搜索'iphone'
chrome.find_element(By.NAME, 'keyword').send_keys('小米6')
chrome.find_element(By.CLASS_NAME, 'btn1').click()
# 4、点击商品图片
chrome.find_element(By.XPATH, '/html/body/div[3]/div[2]/div[3]/div[2]/div[1]/a/img').click()
# 5、窗口切换
# 1、找到新窗口的名字
new_window = chrome.window_handles[-1]
# 2、切换到新窗口
chrome.switch_to.window(new_window)
# 6、把选择的商品加入购物车
chrome.find_element(By.ID, 'joinCarButton').click() # 此时由于跳转了新窗口,所以无法进行操作
# 7、去购物车结算
chrome.find_element(By.CLASS_NAME, 'other_join').click()
# 8、点击结算 css selector 定位方式:在两个class之前需要加.
chrome.find_element(By.CSS_SELECTOR, '.shopCar_btn_03.fl').click()
# 9、添加新地址
chrome.find_element(By.CLASS_NAME, 'add-address').click()
# 10、填写收货人信息
chrome.find_element(By.NAME, 'address[address_name]').send_keys('XC')
chrome.find_element(By.NAME, 'address[mobile]').send_keys('15910100202')
# 11、选择地区的下拉框
sheng=chrome.find_element(By.ID,'add-new-area-select')# 将省这个下拉框进行实例化
Select(sheng).select_by_visible_text('北京市')# 将实例化的下拉框进行类型强制转换成Select型,再使用下拉框的属性进行选择
# 12、选择收货地区--市 由于下拉框中的ID是动态变化的,且class name又是同名的,所以使用find_elements来找到相同class name,再使用标签名来组合
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()
#使用alert.text获取弹框的文字
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()
#使用alert.text获取弹框的文字
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 元素
iframe_element = driver.find_element_by_css_selector("iframe-selector")
# 切换到 iframe 上下文
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
# 切换到第一个 iframe 上下文
driver.switch_to.frame(0)
# 在第一个 iframe 上下文中执行操作
# 切回默认上下文
driver.switch_to.default_content()
# 切换到第二个 iframe 上下文
driver.switch_to.frame(1)
# 在第二个 iframe 上下文中执行操作
# 切回默认上下文
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)
# 切换至第—个iframe
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
# 打开本地的html文件,前面要加fiLe://
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.switch_to.frame(1)
driver.find_element(By.ID,'open-self').click()
#用name#
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)
# 先定位到iframe,在switch_to到指定元素
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
# 进入iframe
ele = driver.find_element(By.CSS_SELECTOR, "body > iframe")
driver.switch_to.frame(ele)
# 退出iframe,切换到上一级
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
# 获取input文件上传元素
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()

# 获取input文件上传元素
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
#访问xxxx网站
driver.get("http://www.xxxx.cn/")
#将用户名密码写入浏览器cookie
driver.add_cookie({'name':'Login_UserNumber', 'value':'username'})
driver.add_cookie({'name':'Login_Passwd', 'value':'password'})
#再次访问xxxx网站,将会自动登录
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)
# tab 键
kw.send_keys(Keys.TAB)
# 刷新
kw.send_keys(Keys.F5)

滚动条

Selenium 执行 JavaScript 脚本

在web自动化,经常会遇到页面显示内容太多的时候,页面就会出现滚动条,一般有两种方式进行下拉,一种是直接下拉到底部/顶部/中部,或者直接给定元素,直接下拉到指定元素的位置。

WebDriver有两个方法来执行JavaScript,分别是:

  • execute_seript 同步执行

  • execute_async_script 异步执行

通过 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
# 指定向下滑动500像素
self.driver.execute_script("window.scrollBy(0, 500);")

# 指定向上滑动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

# 定义执行 JavaScript 的函数
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:
# 暂时设置移动 1/5
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

# 创建 Chrome 浏览器实例
driver = webdriver.Chrome()

# 打开目标网页
driver.get("https://example.com")

# 获取需要滚动的元素
target_element = driver.find_element_by_css_selector("#target-element")

# 创建一个 ActionChains 对象
actions = ActionChains(driver)

# 使用 ActionChains 执行缓动下滑操作
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
# 获取当前屏幕截图的 Base64 编码字符串
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, 这项功能意味着:当鼠标的指针在屏幕的最坐上方,程序会报错;目的是为了防止程序无法停止;

停顿功能

1
pyautogui.PAUSE = 1

意味着所有pyautogui的指令都要暂停一秒;其他指令不会停顿;这样做,可以防止键盘鼠标操作太快;

鼠标操作

控制鼠标移动

获得屏幕分辨率

1
2
3
4
5
6
# 返回所用显示器的分辨率; 
# 输出:Size(width=1920, height=1080)
print(pyautogui.size())
width,height = pyautogui.size()
print(width,height)
# 1920 1080

移动鼠标

1
2
3
4
5
pyautogui.moveTo(100,300,duration=1)
# 按方向移动,左右正负值对应右左,上下正负值对应下上
# moveRel():这是PyAutoGUI库中的一个函数,用于模拟相对于当前鼠标位置的移动操作。
# 第一个参数是左右移动像素值,第二个是上下,向右移动100px,向下移动500px, 这个过程持续 1 秒钟;
pyautogui.moveRel(100,500,duration=1)

获取鼠标位置

1
2
print(pyautogui.position())   
# 得到当前鼠标位置;输出:Point(x=200, y=800)

控制鼠标点击

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
# 按方向拖动
# 向右拖动100px,向下拖动500px, 这个过程持续 1 秒钟;
pyautogui.dragRel(100,500,duration=4) # 第一个参数是左右移动像素值,第二个是上下,

控制鼠标滚动

控制鼠标滚动的函数是scroll(), 传入一个整数的参数,说明向上或向下滚动多少个单位;单位根据操作系统不同而不同;

1
2
# 向上滚动300个单位;
pyautogui.scroll(300)

屏幕处理

获取屏幕截图

我们控制鼠标的操作,不能盲目的进行,所以我们需要监控屏幕上的内容,从而决定要不要进行对应的操作, 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) # Box(left=1280, top=344, width=22, height=22)

# 图像识别(多个)
btm = pyautogui.locateAllOnScreen('zan.png')
print(list(btm)) # [Box(left=1280, top=344, width=22, height=22), Box(left=25, top=594, width=22, height=22)]

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') # 按下shift
pyautogui.press('4') # 按下 4
pyautogui.keyUp('shift') # 释放 shift
# 缓慢的输出$:
pyautogui.typewrite('$$$$', 0.5)

键盘特殊按键

有时我们需要输入一些特殊的按键,比如向左的箭头,这些有相对应的键盘字符串表示,例如:

1
2
# 输出:This
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

项目

练习一

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
# 1)装饰器
# 2)自动化关键数据记录:截图、日志、page_source
import time
import allure
from selenium import webdriver
from selenium.webdriver.common.by import By

# 装饰器的外函数需要一个形参代表函数对象
# 问题:需要通过driver实例截图和打印page_source,装饰器需要先去获取driver实例对象
# 解决:
def ui_exception_record(func):
def inner(*args, **kwargs): # 内函数
# 获取被装饰方法的self,如def test_baidu(self)方法中的self,即实例对象
# 通过self就可以拿到声明的实例变量
# 前提条件,被装饰的方法是一个实例方法,实例需要有实例变量self.driver
# 注意:要保证使用装饰器时,driver已经声明,在用例类里面的前置操作对self.driver声明,也可以将driver = args[0].driver放在except Exception里面的第一行
# driver = args[0].driver
try:
# 当被装饰方法/函数发生异常时就捕获并做数据记录
return func(*args, **kwargs) # 这里会执行被装饰函数
except Exception:
driver = args[0].driver
# 出现异常时的处理,截图操作
timestamp = int(time.time()) # 获取当前时间的时间戳
# 提前创建好image和pagesource路径
image_path = f"./image/image_{timestamp}.PNG"
page_source_path = f"./page_source/page_source_{timestamp}.html "
# 截图
driver.save_screenshot(image_path)
# 记录page_source,将获取的page_source写入到record.html
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)
# 将pagesource记录放到报告中
# 想要html的源码可以将attachment_type=allure.attachment_type.TEXT
allure.attach.file(page_source_path, name="pagesource", attachment_type=allure.attachment_type.TEXT)
# 问题1:异常处理会影响用例本身的结果,如本来就是有异常的用例,却通过了
# 解决:在exception之后再把异常抛出
raise Exception
return inner # 返回内函数,不需要加括号


# 问题2:异常捕获处理代码和业务代码无关,不能耦合,如有多条用例就要写多个异常捕获
# 解决:使用装饰器装饰用例或者相关方法,就不会体现在源码中了
class TestBaidu:
# 前置操作
def setup(self):
self.driver = webdriver.Chrome() # driver声明

# 后置操作
def teardown(self):
self.driver.quit()

# 如果装饰器中的try没有return,被装饰方法有返回值时,会丢失返回值,其他方法调用就没有返回值
# @ui_exception_record
# def find(self):
# return self.driver.find_element(By.ID, "su") # 返回find_element对象

@ui_exception_record # 调用上面的装饰器
def test_baidu(self):
self.driver.get("https://www.baidu.com/")
# self.find().click() # 调用find()方法的返回对象,并点击
self.driver.find_element(By.ID, "su").click() # 定位"百度一下"按钮

练习二

Jpress项目

  • testcases 测试用例
  • data 测试数据
  • logs log 日志
  • config 配置文件
  • reports 测试报告
  • screenshots 截屏
  • lib 第三方库
  • util 类