Web自动化
Elora Rinta!

自动化测试解决的问题:回归测试、压力测试、兼容性测试。

自动化测试的误区:自动化测试可以完全代替手工测试,自动化测试一定比手工测试更厉害,自动化测试可以捕获更多的bug,自动化测试适用于所有功能

什么样的web项目适合做自动化测试?

  1. 需求变动不频繁
  2. 项目周期长
  3. 项目需要回归测试

自动化测试在什么阶段开始–>功能测试完毕(手工测试)

web自动化属于黑盒测试(功能测试)

元素定位

元素定位就是通过元素的信息或元素层级结构来定位

selenium提供的8种定位元素方式

1
2
3
4
5
6
7
8
1. id
2. name
3. class_name
4. tag_name(<标签名.../>)
5. link_text(定位超链接a标签)
6. partial_link_text(定位超链接a标签 模糊)
7. XPath(基于元素路径)
8. CSS(元素选择器)

id定位

html规定id属性在整个html文档中必须是唯一的

el=driver.find_element(by=By.ID, value='id值')

打开百度输入内容进行查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 导包
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
# 获取浏览器对象
browser = webdriver.Chrome()
# 打开url
browser.get("https://www.baidu.com")
# 通过id定位搜索框
el = browser.find_element(by=By.ID, value='kw')
# 搜索框输入内容
el.send_keys('怪物猎人')
# 通过id查找搜索按钮并点击
browser.find_element(by=By.ID, value='su').click()
time.sleep(20)
browser.quit()

name定位

html中name属性值是可以重复的

1
2
3
4
5
6
7
8
9
10
11
12
13
import time
from selenium import webdriver
from selenium.webdriver.common.by import By

browser = webdriver.Chrome()
browser.get("https://www.baidu.com")
browser.find_element(by=By.NAME,value='wd').clear()
el = browser.find_element(by=By.NAME, value='wd')
el.send_keys('索尼')
browser.find_element(by=By.ID, value='su').click()
time.sleep(20)
browser.quit()

class name定位

根据元素class属性值定位元素,html通过class来定义元素的样式。如果class有多个属性值,只使用其中一个

1
2
3
4
5
6
7
8
9
import time
from selenium import webdriver
from selenium.webdriver.common.by import By

browser = webdriver.Chrome()
browser.get("https://www.bilibili.com/")
browser.find_element(by=By.CLASS_NAME, value='channel-link').click() # 有多个相同元素,返回第一个
time.sleep(20)
browser.quit()

查找第三个

1
2
3
4
5
6
7
8
9
import time
from selenium import webdriver
from selenium.webdriver.common.by import By

browser = webdriver.Chrome()
browser.get("https://www.bilibili.com/")
browser.find_elements(by=By.CLASS_NAME, value='channel-link')[2].click()
time.sleep(20)
browser.quit()

tag_name定位

通过标签名来定位,一般有多个。如果存在多个相同标签则返回第一个

1
2
3
4
5
6
7
8
import time
from selenium import webdriver
from selenium.webdriver.common.by import By

dr = webdriver.Chrome()
dr.get('https://www.csdn.net/')
dr.find_elements(By.TAG_NAME, 'dl')[0].click()
time.sleep(20)

定位a标签,link_text定位元素的内容必须全部匹配(不然会报错)

1
2
3
4
5
6
7
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
browser = webdriver.Chrome()
browser.get('https://rinta.top/')
browser.find_element(By.LINK_TEXT,'Python').click()
time.sleep(20)

模糊匹配linktext定位找到元素,但是要保证唯一性

1
2
3
4
5
6
7
8
9
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
browser = webdriver.Chrome()
browser.get('https://rinta.top/')
browser.find_element(By.LINK_TEXT,'Python').click()
time.sleep(5)
browser.find_element(By.PARTIAL_LINK_TEXT,'AR').click()
time.sleep(20)

Xpath

xpath是XML路径定位器,HTML与XML相似,所以也可以用xpath来定位。针对定位到多个元素时也可以下标取值,下标从1开始

1
2
3
//*[text()="xxx"]     # 文本内容是xxx的元素
//*[contains(@attribute,'xxx')] # 属性中含有xxx的元素
//*[starts-with(@attribute,'xxx')] # 属性以xxx开头的元素
表达式 描述
nodename 选取此节点的所有子节点
/ 从当前节点选取直接子节点
// 从当前节点选取子孙节点
. 选取当前节点
. . 选取当前节点的父节点
@ 选取属性
* 任何元素

通过路径定位

绝对路径:以/开头,不能跳级

相对路径:以//开头,后跟元素名称,不知道名称可以用*代替(*代表所有元素)

1
2
3
4
5
6
7
8
9
10
import time
from selenium import webdriver
from selenium.webdriver.common.by import By

dr = webdriver.Chrome()
dr.get('https://www.bilibili.com/')
dr.find_element(By.XPATH, value='//*[@id="i_cecream"]/div[2]/div[1]/div[3]/div[2]/div[1]/a[8]').click()# @为添加属性,//开头为相对路径
# value='//*[@class="channel-items__left"]/a[8]
time.sleep(20)
//*

利用元素属性

dr.find_element(By.XPATH, value='//*[@id="i_cecream"]/div[2]/div[1]/div[3]/div[2]/div[1]/a[8]').click()# @修饰属性

属性和逻辑结合

通过and连接两个属性

//*[@id='id值' and @属性='属性值']

层级与属性结合

//*[@id='父级id值' ]/input

css选择器

css用来描述html元素的显示样式,在css中,选择题是一种模式用于选择需要添加样式的元素。

css定位常用策略:id选择器、class选择器、元素选择器、属性选择器、层级选择器

符号.代表class, 符号 # 代表id

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# id选择器
# 导包
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
# 获取浏览器对象
browser = webdriver.Chrome()
# 打开url
browser.get("https://www.baidu.com")
# 通过css定位搜索框,#代表id
el = browser.find_element(by=By.CSS_SELECTOR, value='#kw')
# 搜索框输入内容
el.send_keys('怪物猎人')
time.sleep(20)
browser.quit()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 导包
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
# 获取浏览器对象
browser = webdriver.Chrome()
# 打开url
browser.get("https://www.bilibili.com/")
# 通过css定位搜索框,#代表id,结合xpath
el = browser.find_element(by=By.CSS_SELECTOR, value='#i_cecream > div.bili-feed4 > div.bili-header.large-header > div.bili-header__channel > div.right-channel-container > div.channel-items__left > a:nth-child(6)'
# el = browser.find_element(by=By.CSS_SELECTOR, value='.channel-items__left > a:nth-child(6)')
# 搜索框输入内容
el.click()
time.sleep(20)
browser.quit()

定位下拉框

通过css定位

[属性名=属性值]定位

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
# 导包
from selenium import webdriver
from selenium.webdriver.common.by import By
import time

from selenium.webdriver.support.select import Select

# 获取浏览器对象
browser = webdriver.Chrome()
time.sleep(2)
# 最大化窗口
browser.maximize_window()
# 隐式等待,每次查找元素前等待
browser.implicitly_wait(10)
browser.get("https://www.ke.com/city/")
time.sleep(5)

time.sleep(3)
# 找到下拉框元素
el = browser.find_elements(By.CSS_SELECTOR, value='.chang-city')[0]
el.click()
# []来指定属性值
time.sleep(3)
browser.find_element(By.CSS_SELECTOR, '[data-province_id="130000"]').click()
time.sleep(10)
browser.quit()

通过select类定位

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
# value
# 导包
from selenium import webdriver
from selenium.webdriver.common.by import By
import time

from selenium.webdriver.support.select import Select

# 获取浏览器对象
browser = webdriver.Chrome()
time.sleep(2)
browser.maximize_window()
browser.implicitly_wait(10)
browser.get(
"https://signup.live.com/signup?lcid=1033&wa=wsignin1.0&rpsnv=13&ct=1667977589&rver=7.0.6737.0&wp=MBI_SSL&wreply=https%3a%2f%2foutlook.live.com%2fowa%2f%3fnlp%3d1%26signup%3d1%26RpsCsrfState%3d03e6f63b-7b44-e3a7-1cec-67445341a6de&id=292841&CBCXT=out&lw=1&fl=dob%2cflname%2cwld&cobrandid=90015&lic=1&uaid=5cd6f6e2004b4d5f96bcb3f20e670a07")
time.sleep(5)
browser.find_element(By.CSS_SELECTOR, value='#iSignupAction').click()
time.sleep(3)
# 找到下拉框元素
# 使id属性定位下拉框
el = browser.find_element(By.CSS_SELECTOR, value='#LiveDomainBoxList')
# 使name属性定位下拉框
el = browser.find_element(By.CSS_SELECTOR, value='[name = LiveDomainBoxList]')
# option通过value定位
Select(el).select_by_value('hotmail.com')
time.sleep(10)
browser.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
# index
# 导包
from selenium import webdriver
from selenium.webdriver.common.by import By
import time

from selenium.webdriver.support.select import Select

# 获取浏览器对象
browser = webdriver.Chrome()
time.sleep(2)
browser.maximize_window()
browser.implicitly_wait(10)
browser.get(
"https://signup.live.com/signup?lcid=1033&wa=wsignin1.0&rpsnv=13&ct=1667977589&rver=7.0.6737.0&wp=MBI_SSL&wreply=https%3a%2f%2foutlook.live.com%2fowa%2f%3fnlp%3d1%26signup%3d1%26RpsCsrfState%3d03e6f63b-7b44-e3a7-1cec-67445341a6de&id=292841&CBCXT=out&lw=1&fl=dob%2cflname%2cwld&cobrandid=90015&lic=1&uaid=5cd6f6e2004b4d5f96bcb3f20e670a07")
time.sleep(5)
browser.find_element(By.CSS_SELECTOR, value='#iSignupAction').click()
time.sleep(3)
# # 找到下拉框元素
el = browser.find_element(By.CSS_SELECTOR, value='#LiveDomainBoxList')
# 使用index定位下拉框
Select(el).select_by_index(1)
time.sleep(10)
browser.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
# visible_text
# 导包
from selenium import webdriver
from selenium.webdriver.common.by import By
import time

from selenium.webdriver.support.select import Select

# 获取浏览器对象
browser = webdriver.Chrome()
time.sleep(2)
browser.maximize_window()
browser.implicitly_wait(10)
browser.get(
"https://signup.live.com/signup?lcid=1033&wa=wsignin1.0&rpsnv=13&ct=1667977589&rver=7.0.6737.0&wp=MBI_SSL&wreply=https%3a%2f%2foutlook.live.com%2fowa%2f%3fnlp%3d1%26signup%3d1%26RpsCsrfState%3d03e6f63b-7b44-e3a7-1cec-67445341a6de&id=292841&CBCXT=out&lw=1&fl=dob%2cflname%2cwld&cobrandid=90015&lic=1&uaid=5cd6f6e2004b4d5f96bcb3f20e670a07")
time.sleep(5)
browser.find_element(By.CSS_SELECTOR, value='#iSignupAction').click()
time.sleep(3)
# # 找到下拉框元素
el = browser.find_element(By.CSS_SELECTOR, value='#LiveDomainBoxList')
# 使用visible_text属性定位下拉框(文本)
Select(el).select_by_visible_text('hotmail.com')
time.sleep(3)
Select(el).select_by_visible_text('outlook.com')
time.sleep(10)
browser.quit()

警告框处理

Selenium中对处理弹出框的操作,有专用的处理方法,并且处理方式都一样

1
2
3
4
5
6
1. 获取弹出框对象
alert=driver.switch_to.alert
2. 调用
alert.text ---> 返回alert/confirm/prompt中文字信息
alert.accept() --->接受对话框选项
alert.dismiss() --->取消对话框选项

aler警告框

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
# 导包
from selenium import webdriver
from selenium.webdriver.common.by import By
import time

from selenium.webdriver.support.select import Select

# 获取浏览器对象
browser = webdriver.Chrome()
time.sleep(2)
browser.maximize_window()
browser.implicitly_wait(20)
# 访问url
browser.get("https://www.runoob.com/try/try.php?filename=tryjs_alert")
time.sleep(5)
# 点击运行按钮
browser.switch_to.frame("iframeResult")
browser.find_element(By.CSS_SELECTOR, '[value="显示警告框"]').click()
time.sleep(3)
# 切换到alert框内
alert = browser.switch_to.alert # 返回alert对话框对象
# 获取文本
print(alert.text)
# 点击确定
alert.accept()

time.sleep(10)
browser.quit()

confirm确认框

prompt提示框

元素操作

.click()单击输入

clear()清楚文本

sendkeys()模拟输入,如果要上传本地文件也使用这个

获取元素信息的常用方法

size 返回元素大小

text 获取元素文本

get_attribute("xxx") 获取属性值,传递到参数为元素的属性名

is_displayed() 判断元素在页面上是否可见。

is_enabled() 判断元素是否可用,即元素是否可以进行交互操作,比如点击、输入等

is_selected() 判断元素是否选择,用来检查复选框或者单选按钮是否被选中

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
# 导包
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
# 获取浏览器对象
browser = webdriver.Chrome()
time.sleep(2)
browser.maximize_window()
# 打开url

browser.get("https://www.bilibili.com/")
# 获取元素大小
print(browser.find_element(By.CSS_SELECTOR,value='.nav-search-content').size)
# 获取元素个数
print(len(browser.find_elements(By.CSS_SELECTOR,value='.channel-link')))
time.sleep(2)
el1=browser.find_element(By.CSS_SELECTOR,value='.nav-search-input')
el1.send_keys('python')
time.sleep(3)
browser.refresh()
el=browser.find_elements(By.CSS_SELECTOR,value='.channel-link')[0]
print(el.text)
time.sleep(3)
# 打印a元素的href属性
print(el.get_attribute('href'))
el.click()
time.sleep(3)
# 判断元素是否可见
el2=browser.find_elements(By.CLASS_NAME,value='channel-link')[3]
print(el2.is_displayed())
time.sleep(3)
# 元素可见,点击
if el2.is_displayed():
el2.click()
time.sleep(3)
# 判断元素是否可用
el3=browser.find_elements(By.CLASS_NAME,value='channel-link')[4]
print(el3.is_enabled())
if el3.is_enabled():
el3.click()

time.sleep(3)
# 关闭主窗口
browser.close()

time.sleep(5)
browser.quit()

浏览器常用操作

maximize_window()最大化浏览器窗口

set_window_size(width,height)设置浏览器窗口大小

set_window_position设置浏览器窗口位置

back()模拟浏览器后退

forward()前进

refresh()刷新,在cookie中使用到

close()关闭当前窗口

quit()关闭浏览器驱动对象(会关闭所有窗口)

title获取页面title

current_url获取当前页面url

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 实现前进后退
# 导包
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
# 获取浏览器对象
browser = webdriver.Chrome()
# 设置窗口位置
browser.set_window_position(320,150)
time.sleep(2)
browser.maximize_window()
# 打开url
browser.get("https://www.baidu.com/")
browser.get("https://www.bilibili.com/")

time.sleep(2)
# 执行后退
browser.back()
time.sleep(2)
# 执行前进
browser.forward()
time.sleep(2)
browser.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
# 导包
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
# 获取浏览器对象
browser = webdriver.Chrome()
time.sleep(2)
browser.maximize_window()
# 打开url

browser.get("https://www.bilibili.com/")
# print(browser.find_element(By.CSS_SELECTOR,value='nav-search-content').size)
time.sleep(2)
el1=browser.find_element(By.CSS_SELECTOR,value='.nav-search-input')
el1.send_keys('python')
time.sleep(3)
browser.refresh()
el=browser.find_elements(By.CLASS_NAME,value='channel-link')[1]

time.sleep(2)
el.click()
print(browser.current_url)
time.sleep(2)
browser.find_elements(By.CLASS_NAME,value='channel-link')[3].click()
time.sleep(3)
browser.find_elements(By.CLASS_NAME,value='channel-link')[4].click()
time.sleep(3)
# 关闭主窗口
browser.close()

time.sleep(5)
browser.quit()

鼠标和键盘操作

鼠标操作

click()是元素的事件,不是鼠标的事件。在selenium中操作鼠标的方法封装在ActionChains类中

实例化对象:action=ActionChains(driver)

方法:

context_click(element) 右击

double_click(element)双击

drag_and_drop(source,target) 拖动

move_to_element悬停

perform()执行以上所有鼠标操作,所有的方法都需要执行才生效

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
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
import time

browser=webdriver.Chrome()
action=ActionChains(browser)
browser.get('https://www.bilibili.com')
el1 = browser.find_element(By.CSS_SELECTOR, value='.nav-search-input')
# 右击鼠标
action.context_click(el1).perform()
el1.send_keys('python')
time.sleep(3)
# 双击
action.double_click(el1).perform()
time.sleep(3)
browser.refresh()
el2 = browser.find_element(By.CSS_SELECTOR, value='.download-client-trigger__icon')
# 悬停
time.sleep(3)
action.move_to_element(el2).perform()

time.sleep(5)
browser.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
# 导包
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
import time

# 获取浏览器对象
browser = webdriver.Chrome()
time.sleep(2)
browser.maximize_window()
# 打开url
action = ActionChains(browser)
browser.get("https://www.baidu.com/")
time.sleep(2)
el1 = browser.find_element(By.CSS_SELECTOR, value='.title-content-title')
el2=browser.find_element(By.CSS_SELECTOR,value='#kw')
time.sleep(3)
# 将元素el1拖至el2
action.drag_and_drop(el1,el2).perform()
time.sleep(3)
browser.find_element(By.CSS_SELECTOR,value='#su').click()
time.sleep(5)
browser.quit()

drag_and_drop_by_offset(source,xoffset,yoffset)通过坐标偏移量执行拖拽

实例化匿名:ActionChains(driver).double_click(element).perform()

实名:action=ActionChains(driver)

键盘操作

常用的键盘操作:

send_keys(Keys.BACK_SPACE) 删除键

send_keys(Keys.SPACE) 空格键

send_keys(Keys.TAB) 制表键

send_keys(keys.ESCAPE) 回退键

send_keys(keys.ENTER) 回车键

send_keys(keys.CONTROL,'a') ctrl+a

send_keys(keys.CONTROL,'c') ctrl+c

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
# 导包
from selenium import webdriver
from selenium.webdriver import ActionChains, Keys
from selenium.webdriver.common.by import By
import time

# 获取浏览器对象
browser = webdriver.Chrome()
time.sleep(2)
browser.maximize_window()
# 打开url
action = ActionChains(browser)
browser.get("https://www.baidu.com/")
time.sleep(2)
el1 = browser.find_element(By.CSS_SELECTOR, value='#kw')
time.sleep(3)
el1.send_keys('Python1')
time.sleep(3)
el1.send_keys(Keys.BACK_SPACE)
time.sleep(3)
el1.send_keys(Keys.CONTROL, 'a')
time.sleep(3)
el1.send_keys(Keys.CONTROL, 'c')
time.sleep(3)
# 粘贴
el1.send_keys(Keys.CONTROL, 'v')
el1.send_keys(Keys.CONTROL, 'v')
browser.find_element(By.CSS_SELECTOR, value='#su').click()
time.sleep(5)
browser.quit()

元素等待

在定位页面元素如果未找到,会在指定时间内一直等待的过程。在设置的时长内加载出来,会执行代码;没有加载出来则抛出异常

为什么要设置元素等待?网络速度慢、电脑配置低、服务器处理请求慢

隐式等待

隐式等待为全局设置,设置一次,作用于所有元素。一般为前置必写代码

定位元素时,如果能定位到元素则直接返回该元素,不触发等待;如果不能定位到该元素,则间隔一段时间后再去定位元素;如果在达到最大时长时还没有找到指定元素,则抛出元素不存在的异常NoSuchElementException

方法:driver.implicitly_wait(timeout),timeout为等待最大时长,单位秒。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 导包
from selenium import webdriver
from selenium.webdriver.common.by import By
import time

# 获取浏览器对象
browser = webdriver.Chrome()
time.sleep(2)
browser.maximize_window()
# 打开url
browser.get("https://www.baidu.com/")
# 设置隐式等待10s
browser.implicitly_wait(10)
time.sleep(2)
# 正确id为kw
el1 = browser.find_element(By.CSS_SELECTOR, value='#kj')
el1.send_keys('python')

显式等待

对单个的元素有效

定位元素时,如果能定位到元素则直接返回该元素,不触发等待;如果不能定位到该元素,则间隔一段时间后再去定位元素;如果在达到最大时长时还没有找到指定元素,则抛出超时异常TimeoutException

1
2
3
4
5
6
7
8
9
10
1. 导包
2. webDriverWait(driver, timeout, poll_frequency=0.5)
1) driver: 浏览器驱动对象
2) timeout: 超时的时长单位秒
3) poll_frequency: 检测间隔时间,默认0.5s
3. 调用方法 until(method):直到...时
1) method: 函数名称,该函数用来实现对元素的定位
2) 一般使用匿名函数来实现:lambda x: x.find_element(By.方法,value)
4. element = WebDriverWait(driver, 10, 1).util(lambda x: x.find_element(By.,value))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 导包
from selenium import webdriver
from selenium.webdriver.common.by import By
import time

from selenium.webdriver.support.wait import WebDriverWait

# 获取浏览器对象
browser = webdriver.Chrome()
time.sleep(2)
browser.maximize_window()
# 打开url
browser.get("https://www.baidu.com/")
# 设置显式等待10s
# 正确id为kw
el = WebDriverWait(browser, 10, poll_frequency=0.5).until(lambda x: x.find_element(By.CSS_SELECTOR, value='#kj'))
# 此时el还不是元素,代码运行起来才是
el.send_keys('python')

区别

  • 显示等待针对单个元素生效,隐式等待针对全局元素生效

滚动条操作

html页面元素为动态显示,元素根据滚动条的下拉而加载。

Selenium没有提供操作滚动条的方法,但是提供了可执行JS脚本的方法,我们通过JS脚本来达到操作滚动条的目的

1
2
3
4
5
1. 设置JS脚本控制滚动条
js = "window.scrollTo(0,1000)"
(0:左边距;1000:上边距 单位:像素)
2. selenium调用JS脚本的方法
driver.execute_scrip(js)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 导包
from selenium import webdriver
from selenium.webdriver.common.by import By
import time

from selenium.webdriver.support.select import Select

# 获取浏览器对象
browser = webdriver.Chrome()
time.sleep(2)
browser.maximize_window()

# 访问url
browser.get("https://rinta.top")
time.sleep(5)
# 设置js控制语句
js = "window.scrollTo(0,10000)"
# 滚动条下拉
browser.execute_script(js)
time.sleep(3)
time.sleep(10)
browser.quit()

frame切换

frame是html页面中的一种框架,主要作用是在当前页面的指定区域显示一页面元素

1
2
3
4
5
6
7
形式一:
<frameset cols="25%,75%">
<frame src="frame_a.htm"></frame>
<fram src="frame_.htm"></fram>
</frameset>
形式二:
<iframe name="iframe_a" src="demo_iframe.htm" width="200",height="200"></iframe>

frame切换方法

1
2
3
4
1. 切换到指定frame的方法,frame_reference可以为frame框架的name、id或者定位到的frame元素
driver.switch_to.frame(frame_reference)
2. 恢复默认页面,在frame中操作其他页面一定要切换到默认页面
driver.switch_to.default_content()

窗口切换

Selenium的默认焦点是在主窗口。在selenium中封装了获取当前窗口的句柄、获取所有窗口句柄和切换到指定句柄窗口的方法

句柄:handle,窗口的唯一识别码

1
2
3
4
5
6
# 获取当前窗口句柄
driver.current_window_handle
# 获取所有窗口句柄
driver.window_handles
# 切换指定句柄窗口
driver.switch_to.window(handle)
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
# 导包
from selenium import webdriver
from selenium.webdriver.common.by import By
import time


# 获取浏览器对象
browser = webdriver.Chrome()
time.sleep(2)
browser.maximize_window()

# 访问url
browser.get("https://www.baidu.com/")
time.sleep(5)
# 输入内容并查询
browser.find_element(By.CSS_SELECTOR, '#kw').send_keys('小润')
browser.find_element(By.CSS_SELECTOR, '#su').click()
time.sleep(3)
ch = browser.current_window_handle
print(ch)
# 点击词条
browser.find_element(By.CSS_SELECTOR, '._around-mask_bo7t2_14').click()
# 获取所有窗口句柄
handles = browser.window_handles
print(handles)
time.sleep(3)
for handle in handles:
# 切换窗口,虽然看到的页面是新打开的小润页面,但是元素定位仍在主窗口
if handle != ch:
browser.switch_to.window(handle)
browser.find_elements(By.CSS_SELECTOR, '.text_BqlxX')[2].click()
time.sleep(10)
browser.quit()

窗口截图与验证码

窗口截图

在执行出错的时候对当前窗口截图保存,可以通过图片直观地看到出错的原因

driver.get_screenshot_as_file(imgpath)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 导包
from selenium import webdriver
from selenium.webdriver.common.by import By
import time


# 获取浏览器对象
browser = webdriver.Chrome()
time.sleep(2)
browser.maximize_window()

# 访问url
browser.get("https://www.baidu.com/")
time.sleep(5)
# 输入内容并查询
browser.find_element(By.CSS_SELECTOR, '#kw').send_keys('小润')
browser.find_element(By.CSS_SELECTOR, '#su').click()
time.sleep(3)
# 截图
browser.get_screenshot_as_file('./screenshot.png')
time.sleep(10)
browser.quit()

根据时间戳获取截图:

browser.get_screenshot_as_file('./{}.png'.format(time.strftime("%Y_%m_%d_%H_%M_%S")))

image-20240315160631218

验证码

selenium中没有对验证码处理的方式

  1. 去掉验证码(测试环境下采用)
  2. 设置万能验证码(生产环境和测试环境下采用)
  3. 验证码识别技术(通过Python-tesseract来识别图片类型验证码,识别率很难达到100%)
  4. 记录cookie(通过cookie进行跳过登录)

cookie是由web服务器生成的,并且保存至用户浏览器上的小型文本文件,可以包含用户相关信息。

cookie数据格式:键值对组成(python中的字典)

cookie产生:客户端请求服务器。如果服务器需要记录该用户状态,就向客户端颁发一个cookie数据

cookie使用:当浏览器再次请求该网站时,浏览器把请求的数据和cookie数据一同提交给服务器检查该cookie,以此来辨认用户状态

Cookie通常用于以下几个方面:

  1. 会话管理:用于跟踪用户的会话信息,例如在用户登录后保持用户的登录状态。
  2. 用户跟踪:用于记录用户的行为和偏好,以便网站提供个性化的体验。
  3. 高级功能:用于实现购物车、记住密码、广告定位等功能。
  4. 安全性:可以用于防止跨站点请求伪造(CSRF)等安全问题。

selenium操作cookie

1
2
3
4
5
6
7
# 1. 获取指定cookie,name为cookie的名称。底层是get_cookies()
get_cookie(name)
# 2. 获取本网站所有本地cookies
get_cookies()
# 3. 添加coockie。cookie_dict:一个字典对象,必选的键包括'name' and 'value'
add_cookie(cookie_dict)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 导包
from selenium import webdriver
from selenium.webdriver.common.by import By
import time

# 获取浏览器对象
browser = webdriver.Chrome()
time.sleep(2)
browser.maximize_window()

# 访问url
browser.get("https://www.baidu.com/")
time.sleep(3)
# 添加cookie,以登录状态进入
browser.add_cookie({"name": "BDUSS",
"value": "JwZG4zOUpyQU1RbXI2Z0VmbXVlU0ZuWmY0MUotcVZqcER-N3RqV0l-WUZveHRtSVFBQUFBJCQAAAAAAAAAAAEAAABZR~-XUmlubnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUW9GUFFvRlSF"})

time.sleep(5)
browser.refresh()
time.sleep(10)
browser.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
# 导包
from selenium import webdriver
from selenium.webdriver.common.by import By
import time

# 获取浏览器对象
browser = webdriver.Chrome()
time.sleep(2)
browser.maximize_window()

# 访问url
browser.get("https://www.baidu.com/")
time.sleep(3)
# 添加cookie
browser.add_cookie({"name": "BDUSS",
"value": "JwZG4zOUpyQU1RbXI2Z0VmbXVlU0ZuWmY0MUotcVZqcER-N3RqV0l-WUZveHRtSVFBQUFBJCQAAAAAAAAAAAEAAABZR~-XUmlubnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUW9GUFFvRlSF"})
# 获取所有的cookies信息
cookies=browser.get_cookies()
for co in cookies:
print(co)
# 获取单个cookie
cookie=browser.get_cookie("name")
print(cookie)
print(browser.get_cookie('BDUSS'))
time.sleep(5)
browser.refresh()
time.sleep(10)
browser.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
52
import time
import unittest
from selenium import webdriver
from selenium.webdriver.common.by import By


# 定义测试类
class TestJDShop(unittest.TestCase):
# 定义初始化方法
def setUp(self):
url = 'https://passport.jd.com/new/login.aspx?ReturnUrl=https%3A%2F%2Fwww.jd.com%2F%3Fcu%3Dtrue%26utm_source%3Dbaidu-pinzhuan%26utm_medium%3Dcpc%26utm_campaign%3Dt_288551095_baidupinzhuan%26utm_term%3D0f3d30c8dba7459bb52f2eb5eba8ac7d_0_40173755626041979e00a100eec87380'

self.driver = webdriver.Chrome()
self.driver.maximize_window()
self.driver.get(url)
self.driver.implicitly_wait(10)

# 定义teardown
def tearDown(self):
# 关闭浏览器对象
time.sleep(2)
self.driver.close()

def test_code_null(self):
driver = self.driver
# 切换至短信登录
driver.find_element(By.CSS_SELECTOR, '#sms-login').click()
# 输入账号
el1 = driver.find_element(By.CSS_SELECTOR, '#mobile-number')
time.sleep(3)
el1.send_keys('12345678901')
# 点击发送验证码
time.sleep(3)
el2 = driver.find_element(By.CSS_SELECTOR, '#send-sms-code-btn')
el2.click()
# 输入验证码
time.sleep(3)
el3 = driver.find_element(By.CSS_SELECTOR, '#sms-code')
el3.send_keys('324678')
# 点击登录
time.sleep(3)
driver.find_element(By.CSS_SELECTOR, '#sms-login-submit').click()
# 获取错误提示信息
text = driver.find_element(By.CSS_SELECTOR, '.sms-box-error-msg').text
expect_text = '验证码为空'
print(text)
try:
self.assertEqual(text, expect_text)
except AssertionError:
driver.get_screenshot_as_file('./{}.png'.format(time.strftime("%Y_%m_%d_%H_%M_%S")))
# 抛异常,有异常捕获不抛出会显示test pass
raise

PO模式

PO:page(页面),objecr(对象)

v1:不采用任何模型(线性模型)

v2:采用unittest框架

v3:业务代码和页面对象进行分离

v4:实际中的PO模式编写

v1

不能实现批量执行

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
# 淘宝界面,用不了
import time

from selenium import webdriver
from selenium.webdriver.common.by import By


# 测试之前的前置工作
# 实例化对象
browser=webdriver.Chrome()
# 窗口最大化
browser.maximize_window()
# 获取url
browser.get('https://login.taobao.com/member/login.jhtml?spm=a21bo.jianhua.754894437.1.5af92a89NKrwFE&f=top&redirectURL=https%3A%2F%2Fwww.taobao.com%2F')
browser.implicitly_wait(10)

# 切换至短信登录
# 点击切换按钮
time.sleep(3)
browser.find_element(By.XPATH,'//*[@id="login"]/div[2]/div/div[2]/a[1]').click()
time.sleep(3)
# 输入错误的username
el1=browser.find_element(By.CSS_SELECTOR,'#fm-login-id')
el1.send_keys('15234567888')
# 输入正确的密码
time.sleep(2)
el2=browser.find_element(By.CSS_SELECTOR,'#fm-login-password')
el2.send_keys('Rinta1343')
# 点击登录
time.sleep(10)
browser.find_element(By.CSS_SELECTOR,'.class="fm-button fm-submit password-login').click()
# 获取错误提示信息
time.sleep(1)
msg=browser.find_element(By.CSS_SELECTOR,'.login-error-msg')
assert msg == '账号名或登录密码不正确'
# 结束工作,关闭对象
time.sleep(5)
browser.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
import time

from selenium import webdriver
from selenium.common import NoSuchElementException
from selenium.webdriver.common.by import By


# 测试之前的前置工作
# 实例化对象
browser=webdriver.Chrome()
# 窗口最大化
browser.maximize_window()
# 获取url
browser.get('http://demo5.tp-shop.cn/')
browser.implicitly_wait(10)

# 跳转到登录页面
time.sleep(3)
# 输入错误的username
browser.find_element(By.LINK_TEXT,'登录').click()
el1=browser.find_element(By.CSS_SELECTOR,'#username')
el1.send_keys('13800138006')
# 输入错误的密码
time.sleep(2)
el2=browser.find_element(By.CSS_SELECTOR,'#password')
el2.send_keys('123456')
# 点击登录
time.sleep(10)
browser.find_element(By.CSS_SELECTOR,'.J-login-submit').click()
# 如果有消息链接,则登录成功,截图
# el3=browser.find_element(By.CSS_SELECTOR,'.J-umsg')
try:
# 尝试定位元素
el3=browser.find_element(By.CSS_SELECTOR,'.J-umsg')
print('登录成功')
browser.get_screenshot_as_file('./success.png')
except NoSuchElementException:
print('登录失败')
browser.get_screenshot_as_file('./fail.png')
# 结束工作,关闭对象
time.sleep(5)
browser.quit()

换成正确密码:

1
el2.send_keys('soubao0316')

缺点:数据和代码操作融合在一起,单线操作

v2

可以批量运行,代码冗余量大。没有实现页面对象与业务脚本的分离

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import time
import unittest
from selenium import webdriver
from selenium.webdriver.common.by import By


# 新建测试类
class TestLogin(unittest.TestCase):
browser = None

# 测试之前的前置工作
@classmethod
def setUpClass(cls):
# 实例化对象
cls.browser = webdriver.Chrome()
# 窗口最大化
cls.browser.maximize_window()
# 获取url
cls.browser.get('http://localhost/iwebshopmaster/')
cls.browser.implicitly_wait(10)

@classmethod
def tearDownClass(cls):
# 结束工作,关闭对象
time.sleep(5)
cls.browser.quit()

def test_login_username_not_exist(self):
browser = self.browser
# 切换至登录界面
time.sleep(3)
browser.find_element(By.LINK_TEXT, '登录').click()
time.sleep(3)
# 输入错误的username
el1 = browser.find_element(By.NAME, 'login_info')
el1.clear()
el1.send_keys('abc')
# 输入正确的密码
time.sleep(2)
el2 = browser.find_element(By.NAME, 'password')
el2.clear()
el2.send_keys('123456')
# 点击登录
time.sleep(10)
browser.find_element(By.CSS_SELECTOR, '.submit_login').click()
# 获取错误提示信息
time.sleep(1)
msg = browser.find_element(By.CSS_SELECTOR, '.prompt')
try:
# 错误的断言import time
import unittest
from selenium import webdriver
from selenium.webdriver.common.by import By


# 新建测试类
class TestLogin(unittest.TestCase):
browser = None

# 测试之前的前置工作
@classmethod
def setUpClass(cls):
# 实例化对象
cls.browser = webdriver.Chrome()
# 窗口最大化
cls.browser.maximize_window()
# 获取url
cls.browser.get('http://localhost/iwebshopmaster/')
cls.browser.implicitly_wait(10)

@classmethod
def tearDownClass(cls):
# 结束工作,关闭对象
time.sleep(5)
cls.browser.quit()

def test_login_username_not_exist(self):
browser = self.browser
# 切换至登录界面
time.sleep(3)
browser.find_element(By.LINK_TEXT, '登录').click()
time.sleep(3)
# 输入错误的username
el1 = browser.find_element(By.NAME, 'login_info')
el1.clear()
el1.send_keys('abc')
# 输入正确的密码
time.sleep(2)
el2 = browser.find_element(By.NAME, 'password')
el2.clear()
el2.send_keys('123456')
# 点击登录
time.sleep(10)
browser.find_element(By.CSS_SELECTOR, '.submit_login').click()
# 获取错误提示信息
time.sleep(1)
msg = browser.find_element(By.CSS_SELECTOR, '.prompt').text
try:
self.assertEqual(msg, '账号名或登录密码不正确')
except AssertionError:
browser.get_screenshot_as_file('./failusername.png')

def test_login_password_err(self):
browser = self.browser
# 切换至短信登录
# 点击切换按钮
time.sleep(3)
browser.find_element(By.LINK_TEXT, '登录').click()
time.sleep(3)
# 输入正确的username
el1 = browser.find_element(By.NAME, 'login_info')
el1.clear()
el1.send_keys('ab')
# 输入错误的密码
time.sleep(2)
el2 = browser.find_element(By.NAME, 'password')
el2.clear()
el2.send_keys('1234567')
# 点击登录
time.sleep(10)
browser.find_element(By.CSS_SELECTOR, '.submit_login').click()
# 获取错误提示信息
time.sleep(1)
msg = browser.find_element(By.CSS_SELECTOR, '.prompt').text
try:
self.assertEqual(msg, '账号或密码错误')
except AssertionError:
browser.get_screenshot_as_file('../reports/failpassword.png')

self.assertEqual(msg, '账号名或登录密码不正确')
except AssertionError:
browser.get_screenshot_as_file('./failusername.png')

def test_login_password_err(self):
browser = self.browser
# 切换至短信登录
# 点击切换按钮
time.sleep(3)
browser.find_element(By.LINK_TEXT, '登录').click()
time.sleep(3)
# 输入正确的username
el1 = browser.find_element(By.NAME, 'login_info')
el1.clear()
el1.send_keys('ab')
# 输入错误的密码
time.sleep(2)
el2 = browser.find_element(By.NAME, 'password')
el2.clear()
el2.send_keys('1234567')
# 点击登录
time.sleep(10)
browser.find_element(By.CSS_SELECTOR, '.submit_login').click()
# 获取错误提示信息
time.sleep(1)
msg = browser.find_element(By.CSS_SELECTOR, '.prompt')
try:
# 正确的断言
self.assertEqual(msg, '账号或密码错误')
except AssertionError:
browser.get_screenshot_as_file('../reports/failpassword.png')

v3

页面层以page开头,业务层以test开头

页面层级清晰,但是代码冗余量大

页面层代码page_login.py:

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
"""
页面对象层
页面对象编写技巧:
类名:使用大驼峰将模块名称抄进来,有下划线去掉下划线
方法:根据业务需求每个操作步骤单独封装一个方法
方法名:page_XXX
"""
from selenium import webdriver
from selenium.webdriver.common.by import By


class PageLogin:
def __init__(self):
# 获取driver对象
self.driver = webdriver.Chrome()
# 最大化浏览器
self.driver.maximize_window()
# 隐式等待
self.driver.implicitly_wait(10)
# 打开url
self.driver.get('http://localhost/iwebshopmaster/')

# 点击登录链接
def page_click_login(self):
self.driver.find_element(By.LINK_TEXT, '登录').click()


# 输入用户名
def page_input_username(self, username):
self.driver.find_element(By.NAME, 'login_info').send_keys(username)

# 输入密码
def page_input_pwd(self, pwd):
self.driver.find_element(By.NAME, 'password').send_keys(pwd)

# 点击登录
def page_click_login_btn(self):
self.driver.find_element(By.CLASS_NAME, 'submit_login').click()

# 获取异常提示
def page_get_text(self):
return self.driver.find_element(By.CSS_SELECTOR, '.prompt').text

# 组装登录业务方法给业务层调用
def page_login(self, username, pwd):
# 切换至登录界面
self.page_click_login()
# 输入用户名
self.page_input_username(username)
# 输入密码
self.page_input_pwd(pwd)
# 点击登录
self.page_click_login_btn()

业务层代码test_login:

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
# 导包
import time
import unittest

from parameterized import parameterized

from po.v3.page.page_login import PageLogin


# 新建测试类
class TestLogin(unittest.TestCase):

# 初始化方法
def setUp(self):
# 创建page对象
self.login = PageLogin()

# 结束方法
def tearDown(self):
# 关闭驱动对象
self.login.driver.quit()

# 新建测试方法
# 参数化
@parameterized.expand([('abc', '123456', '账号不存在'), ('ab', '123123', '密码错误')])
def test_login(self, username, pwd, expect):
# 调用测试登录方法
self.login.page_login(username, pwd)
# 获取登录信息
msg = self.login.page_get_text()
try:
# 断言
self.assertEqual(msg, expect)
except AssertionError:
self.login.driver.get_screenshot_as_file('./{}.png'.format(time.strftime("%Y_%m_%d_%H_%M_%S")))

v4

抽取v3版本的page页面公共方法—>base(基类/工具层)

page(页面对象):一个页面封装成一个对象,继承base

scripts(业务层):导包调用page页面

base/base.py

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
import time

from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.wait import WebDriverWait
from selenium import webdriver


class Base:
chrome_testing_path=r"D:\chrometest\chrome-win64\chrome.exe"
chromedriver_path=r"D:\chrometest\chrome-win64\chromedriver.exe"
options = webdriver.ChromeOptions()
options.binary_location = chrome_testing_path
options.add_experimental_option('detach', True)
service=Service(chromedriver_path)
# 初始化
def __init__(self):
self.driver = webdriver.Chrome(service=self.service,options=self.options)
self.driver.maximize_window()
self.driver.get(
'http://localhost/iwebshopmaster')

# 查找元素方法(提供:点击、输入、获取文本)
def base_find_element(self, loc, timeout=30, poll_frequency=0.5):
# 封装显示等待,会返回元素
return WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll_frequency).until(
lambda x: x.find_element(*loc))

# 点击方法
def base_click(self, loc):
self.base_find_element(loc).click()

# 输入方法
def base_input(self, loc, value):
el = self.base_find_element(loc)
# 清空内容
el.clear()
# 输入
el.send_keys(value)

# 获取文本方法
def base_get_text(self, loc):
return self.base_find_element(loc).text

# 截图方法
def base_get_img(self):
self.driver.get_screenshot_as_file("../img/{}.png".format(time.strftime("%Y_%m_%d_%H_%M_%S")))


page/_init_.py:存放loc(查找元素的参数):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from selenium.webdriver.common.by import By

# 以下为登录页面元素配置信息
# 登录链接
login_link = By.LINK_TEXT, "登录"

# 用户名
login_username = By.NAME, "login_info"
# 密码
login_pwd = By.NAME, "password"
# 点击按钮
login_btn = By.CSS_SELECTOR, ".submit_login"
# 提示信息
login_msg = By.CSS_SELECTOR, ".prompt"

page/page_login.py:定义page类(继承自base)

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
from po.v4 import page
from po.v4.base.base import Base


class PageLogin(Base):
# 点击登录链接
def page_click_login_link(self):
self.base_click(page.login_link)

# 输入用户名
def page_input_username(self, username):
self.base_input(page.login_username, username)

# 输入密码
def page_input_pwd(self, pwd):
self.base_input(page.login_pwd, pwd)

# 点击登录
def page_click_login_btn(self):
self.base_click(page.login_btn)

# 获取异常信息
def page_get_text(self):
return self.base_get_text(page.login_msg)

# 截图
def page_get_screenshot(self):
self.base_get_img()

# 组装页面的操作:输入用户名、密码、点击登录
def page_login(self, username, pwd):
self.page_input_username(username)
self.page_input_pwd(pwd)
self.page_click_login_btn()

scripts:

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
# 导包
import unittest

from parameterized import parameterized

from po.v4.page.page_login import PageLogin


def get_data():
return [('abc', '123456', '账号不存在'), ('ab', '123123', '密码错误')]


# 新建测试类并继承
class TestLogin(unittest.TestCase):
login = None

# setUP
@classmethod
def setUpClass(cls):
# 实例化获取页面对象PageLogin
cls.login = PageLogin()
# 点击登录
cls.login.page_click_login_link()

# tearDown
@classmethod
def tearDownClass(cls):
# 关闭驱动对象
cls.login.driver.quit()

@parameterized.expand(get_data())
def test_login(self, username, pwd, expect):
# 调用登录方法
self.login.page_login(username, pwd)
# 获取登录提示信息
msg = self.login.page_get_text()
# 断言
try:
self.assertEqual(msg, expect)
except AssertionError:
# 截图
self.login.page_get_screenshot()

数据驱动

以数据来驱动整个测试用例的执行,也就是测试数据决定测试结果,可以将用户的关注点放在测试数据的构建和维护上,而不是直接维护脚本,可以利用同样的过程对不同的数据进行测试,实现要依赖参数化。

数据驱动常用的格式:json、xml、excel、csv、txt

json的底层是字符串,和字典有区别

python字典和json之间的转换

python字典–>json字符串

dumps()

1
2
3
4
5
6
7
data = {
'id':1,
'name':'Tom',
'address':'北京市海淀区',
'school':None
}
json_str=json.dumps(data) #jump()是写
1
2
3
4
5
6
7
8
9
10
11
12
import json

data = {
'id': 1,
'name': 'Tom',
'address': '北京市海淀区',
'school': None
}
print(type(data))
json_str = json.dumps(data)
print(json_str)
print(type(json_str))

json字符串–>dict

键名必须在””中

loads()

1
2
json_str = '{"id":1,"name":"Tom","address":"北京市海淀","school":null}'
dict_data=json.loads(json_str)# loads()为读取json
1
2
3
4
5
6
7
import json

json_str = '{"id":1,"name":"Tom","address":"北京市海淀","school":null}'
dict_data=json.loads(json_str)
print(type(json_str))
print(dict_data)
print(type(dict_data))

json读写

写json

dump()

1
2
3
4
5
import json

param = '{"id":1,"name":"Tom","address":"北京市","school":null}'
with open('../test_write.json', 'w', encoding='utf-8') as f:
json.dump(param, f, ensure_ascii=False)

读json

load()

1
2
3
4
5
import json

with open('../test_write.json', 'r', encoding='utf-8') as f:
data = json.load(f)
print(data)

将登录模块改为json数据驱动

test_login.py:

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
# 导包
import json
import unittest

from parameterized import parameterized

from po.v4.page.page_login import PageLogin


def get_data():
# return [('abc', '123456', '账号不存在'), ('ab', '123123', '密码错误')]
with open('../data/test_data.json', 'r', encoding='UTF-8') as f:
data = json.load(f)
test_list=[]
for data in data:
test_tuple= (data.get("username"),data.get("pwd"),data.get("expect"))
test_list.append(test_tuple)
return test_list

# 新建测试类并继承
class TestLogin(unittest.TestCase):
login = None

# setUP
@classmethod
def setUpClass(cls):
# 实例化获取页面对象PageLogin
cls.login = PageLogin()
# 点击登录
cls.login.page_click_login_link()

# tearDown
@classmethod
def tearDownClass(cls):
# 关闭驱动对象
cls.login.driver.quit()

@parameterized.expand(get_data())
def test_login(self, username, pwd, expect):
# 调用登录方法
self.login.page_login(username, pwd)
# 获取登录提示信息
msg = self.login.page_get_text()
# 断言
try:
self.assertEqual(msg, expect)
except AssertionError:
# 截图
self.login.page_get_screenshot()

json文件:

有几组数据就有几个testcase

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[
{
"username": "abc",
"pwd": 123456,
"expect": "账号不存在"
},
{
"username": "ab",
"pwd": 1234567,
"expect": "密码错误"
},
{
"username": "a",
"pwd": 123456,
"expect": "账号不合法"
},
{
"username": "ab",
"pwd": 12345,
"expect": "密码错误"
}
]

注意:操作时间过快会导致截图被覆盖,本来要截四张图变成两张

网页计算器案例

base: 查找元素、点击、获取value属性、截图

base>base.py

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
import time

from selenium.webdriver.support.wait import WebDriverWait


class Base:
# 初始化方法
def __init__(self, driver):
self.driver = driver

# 查找元素
def base_find_element(self, loc, timeout=30, poll=0.5):
"""
:param loc: 元素的定位信息,格式为元组
:param timeout: 默认超时时间,可以修改
:param poll: 访问频率莫,默认0.5
:return: 返回查找的元素
"""
return WebDriverWait(self.driver,
timeout=timeout,
poll_frequency=poll).until(lambda x: x.find_element(*loc))

# 查找元素并点击
def base_click(self, loc):
self.base_find_element(loc).click()

# 获取value
def base_value_get(self, loc):
# get_attribute()获取属性值
return self.base_find_element(loc).get_attribute("value")

def base_get_screenshot(self):
self.driver.get_screenshot_as_file("{}.png".format(time.strftime("%Y_%m_%d_%H_%M_%S")))

base>get_driver.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from selenium import webdriver

from cal_test import page


class GetDriver:
driver=None
@classmethod
def get_driver(cls):
if cls.driver is None:
# 实例化浏览器
cls.driver=webdriver.Chrome()
# 最大化
cls.driver.maximize_window()
# 打开浏览器
cls.driver.get(page.url)
return cls.driver
@classmethod
def quit_driver(cls):
if cls.driver:
cls.driver.quit()
cls.driver=None

data:

1
2
3
4
5
6
7
{
"calc_001":{"a": 1,"b": 2,"expect": 3},
"calc_002":{"a": 33,"b": 12,"expect": 45},
"calc_003": {"a": 1212,"b": 12,"expect": 1224},
"calc_004": {"a": 1211,"b": 11,"expect": 1222},
"calc_005": {"a": 1213,"b": 13,"expect": 1226}
}

page>_init_.py

1
2
3
4
5
6
7
8
9
10
11
12
13
"""以下为计算器配置数据"""
from selenium.webdriver.common.by import By
# 数字键有一定的规律,先暂时不定位此键
# 服务器域名地址
url="http://cal.apple886.com/"
# 加号
calc_add=By.CSS_SELECTOR,'#simpleAdd'
# 等号
calc_equal=By.CSS_SELECTOR,'#simpleEqual'
# 获取结果
calc_res=By.CSS_SELECTOR,'#resultIpt'
# 清平
calc_clear=By.CSS_SELECTOR,'#simpleClearAllBtn'

page>page_calc.py

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
from selenium.webdriver.common.by import By

from cal_test import page
from cal_test.base.base import Base


class PageCalc(Base):

# 点击数字方法
def page_click_num(self, num):
for n in str(num):
# 拆开单个按钮的定位方式
loc = By.CSS_SELECTOR, '#simple{}'.format(n)
self.base_click(loc)

# 点击加号方法
def page_click_add(self):
self.base_click(page.calc_add)

# 点击等号
def page_click_eq(self):
self.base_click(page.calc_equal)

# 获取结果
def page_get_res(self):
return self.base_value_get(page.calc_res)

# 点击清屏
def page_clear(self):
self.base_click(page.calc_clear)

# 截屏
def page_get_screenshot(self):
self.base_get_screenshot()

# 组装
def page_add_calc(self, a, b):
self.page_click_num(a)
self.page_click_add()
self.page_click_num(b)
self.page_click_eq()

scripts:

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
import unittest
from parameterized import parameterized

from cal_test.base.get_driver import GetDriver
from cal_test.page.page_calc import PageCalc
from cal_test.tools.read_json import read_json


def get_data():
datas = read_json("calc.json")
arrs = []
for data in datas.values():
arrs.append((data.get("a"), data.get("b"), data.get("expect")))
return arrs


class TestCalc(unittest.TestCase):
driver = None

# setup
@classmethod
def setUpClass(cls):
# 初始化计算页面对象
cls.driver = GetDriver().get_driver()
cls.calc = PageCalc(cls.driver)

# teardown()
@classmethod
def tearDownClass(cls):
# 关闭driver
GetDriver().quit_driver()

# 测试方法
@parameterized.expand(get_data())
def test_add_calc(self, a, b, expect):
# 调用计算业务方法
self.calc.page_add_calc(a, b)
# 断言

# 截图
try:
self.assertEqual(self.calc.page_get_res(), str(expect))
except AssertionError:
self.calc.base_get_screenshot()
raise

tools>read_json.py:

1
2
3
4
5
6
7
8
9
# 导包
import json


# 调用load方法
def read_json(filename):
filepath="../data/"+filename
with open(filepath, "r", encoding="utf-8") as f:
return json.load(f)

日志

可以查看错误提示信息。日志记录系统运行时的信息

作用:

  • 调试程序
  • 了解系统程序运行的情况是否正常
  • 系统程序运行故障分析与问题定位
  • 用来做用户行为分析和数据统计

日志级别

即日志信息的优先级、重要性或严重程度

日志级别 描述
DEBUG 调试级别,打印非常详细的日志信息,通常用于对代码的的调试
INFO 信息级别,打印一般的日志信息,突出强调程序的运行过程
WARNING 警告级别,打印警告日志信息,潜在错误的情形
ERROR 错误级别,打印错误异常信息,该级别的错误可能导致系统的一些功能无法正常执行
CRITICAL 严重错误级别,一个严重错误,系统可能无法继续运行

为程序指定一个日志级别后,程序会记录所有日志级别大于或等于指定级别的信息。一般建议只使用DEBUG、INFO、WARNING、ERROR

logging模块

1
2
3
4
5
6
7
import logging
# 调用指定级别,输入日志信息
logging.debug("this is a debug !")
logging.info("this is a info")
logging.warning("this is a warning")
logging.error("this is a error")
logging.critical("this is a critical")

默认设置信息级别是warning

设置日志级别:

1
2
3
4
5
6
7
8
9
10
import logging

# 设置级别
logging.basicConfig(level=logging.DEBUG) # DEBUG是给常量,debug是方法
# 调用指定级别,输入日志信息
logging.debug("this is a debug !")
logging.info("this is a info")
logging.warning("this is a warning")
logging.error("this is a error")
logging.critical("this is a critical")

设置日志格式

1
2
3
4
5
6
7
8
9
10
11
import logging
# 设置格式
fmt="%(asctime)s %(levelname)s [%(name)s] [%(filename)s%(funcName)s:%(lineno)d] - %(message)s"
# 设置级别、格式
logging.basicConfig(level=logging.DEBUG,format=fmt) # DEBUG是给常量,debug是方法
# 调用指定级别,输入日志信息
logging.debug("this is a debug !")
logging.info("this is a info")
logging.warning("this is a warning")
logging.error("this is a error")
logging.critical("this is a critical")

保存到文件

1
2
# 设置级别、格式、保存到指定文件
logging.basicConfig(level=logging.DEBUG,format=fmt,filename='..\log\log01.log')

 Comments
Comment plugin failed to load
Loading comment plugin
Powered by Hexo & Theme Keep
This site is deployed on
Total words 75.1k Unique Visitor Page View