
Python自动化测试
第一、二天:学会Python的基本使用
简介
Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。
Python 的设计具有很强的可读性,相比其他语言经常使用英文关键字,其他语言的一些标点符号,它具有比其他语言更有特色语法结构。
1.注释
单行注释: 使用方式:前面加# 也可以用快捷键:鼠标停留在当前句子后,按ctrl+/
多行注释:使用方法:点2次 '单引号
2.变量的声明
命名规则:和Java大概一致
变量的声明:在Java中变量声明需要指定类型,而在Python中则不需要
// Java的声明
String a = "测试";
# python的声明
a = "测试"
3.运算符
算术运算符:+、-、*、/、%、**、//;
# 加
print(10 + 20)
# 执行结果
# 30
# 减
print(10 - 5)
# 执行结果
# 5
# 乘
print(10 * 2)
# 执行结果
# 20
# 除
print(10 / 5)
# 执行结果
# 2.0
# 取整除
print(10 // 3)
# 执行结果
# 3
# 取余
print(10 % 3)
# 执行结果
# 1
# 指数
print(2 ** 3)
# 执行结果
# 8
比较运算符:==、!=、>、<、>=、<=;
# 相等
print(10 == 10)
# 执行结果
# True
# 不等于
print(10 != 10)
# 执行结果
# False
# 大于
print(10 > 10)
# 执行结果
# False
# 小于
print(10 < 10)
# 执行结果
# False
# 大于等于
print(10 >= 10)
# 执行结果
# True
# 小于等于
print(10 <= 10)
# 执行结果
# True
复合赋值运算符:+=、-=、*=、/=、%=、**=、//=
# 加法赋值运算符
a = 1
a += 10
print(a)
# 执行结果
# 11
# 减法赋值运算符
a = 1
a -= 10
print(a)
# 执行结果
# -9
# 乘法赋值运算符
a = 1
a *= 10
print(a)
# 执行结果
# 10
# 除法赋值运算符
a = 1
a /= 10
print(a)
# 执行结果
# 0.1
# 取模赋值运算符
a = 10
a %= 2
print(a)
# 执行结果
# 0
# 幂赋值运算符
a = 2
a **= 3
print(a)
# 执行结果
# 8
# 取整除赋值运算符
a = 10
a //= 3
print(a)
# 执行结果
# 3
4.条件和循环
if功能
if 条件:
代码语句
if a == 0:
print(a)
else功能
if a == 0:
print(a)
else:
print(a)
elif功能 elif等同Java的else if
#if...elif...else 格式
if 条件1:
执行代码1
elif 条件2:
执行代码2
elif 条件3:
执行代码3
elif 条件4:
执行代码4
elif 条件5:
执行代码5
else:
执行代码6
if嵌套
if 判断语句条件1:
满足条件时,执行语句1
满足条件时,执行语句2
满足条件时,执行语句3
......
if 判断语句条件1:
满足条件时,执行语句1
满足条件时,执行语句2
满足条件时,执行语句3
......
while循环
#while格式
while 条件:
条件满足时,做的事情1
条件满足时,做的事情2
条件满足时,做的事情3
...(省略)...
#示例
sum = 0
i = 1
while i <= 100:
sum += i
i += 1
print("sum=%d" % sum)
'''
运行结果为:
sum=5050
'''
while嵌套
while 条件1:
条件1满足时,做的事情1
条件1满足时,做的事情2
条件1满足时,做的事情3
...(省略)...
while 条件2:
条件2满足时,做的事情1
条件2满足时,做的事情2
条件2满足时,做的事情3
...(省略)...
for循环
#for 格式
for 临时变量 in 列表或者字符串等:
循环满足条件时执行的代码
else:
循环不满足条件时执行的代码
#示例
a = "hello world"
#临时变量i来储存遍历时a的基本元素
for i in a:
print(i)
b = ["abc", 123, "efg", 12.8]
for i in b:
print(i)
break和continue
break的作用:用来结束整个循环continue的作用:用来结束本次循环,紧接着执行下一次的循环
#break 示例
a = "hello"
for i in a:
if i == "l":
break
print(i)
'''
运行结果为:
h
e
'''
#continue 示例
a = "hello"
for i in a:
if i == "l":
continue
print(i)
'''
运行结果为:
h
e
o
'''
5.函数与模块
Python 是一种模块化语言,模块是一种组织 Python 代码的方式,有助于提高代码的可重用性。Python 中的模块可以是 Python 文件或 C 语言编写的 Python 扩展模块。要使用模块中定义的函数或变量,需要使用 import 语句将模块导入到当前代码中。函数是 Python 中的一种可重用代码块,通常用于执行特定的任务或计算结果。Python 中定义函数使用 def 语句,函数名称应该具有描述性,并遵循命名规则。函数的参数是函数定义的输入,可以是位置参数或关键字参数。函数定义的返回值是函数的输出。
5.1函数
函数:是组织好的,可重复使用的,用来实现特定功能的代码段
定义:
def 函数名(传入参数):
代码块
return 返回值
# 实例
def add(a,b):
return a+b
注意:如果函数没有使用return语句返回数据,会返回None这个字面量;在if判断中,None等同于False;定义变量,但暂时不需要变量有具体值,可以用None来代替
5.2函数多返回值
def count():
return 1, 2
a, b = count()
print(a)
print(b)
# 执行结果
1
2
按照返回值的顺序,写对应顺序的多个变量接收即可 变量之间用逗号隔开
5.3函数多种传参方式
5.3.1位置参数
调用函数时根据函数定义的参数位置来传递参
def add(a, b):
return a+b
print(add(1,2))
# 执行结果
3
传递的参数和定义的参数的顺序及个数必须一致
5.3.2关键字参数
函数调用时通过“键=值”形式传递参数
def add(a, b):
return a+b
print(add( a=1 , b=2 ))
# 执行结果
3
5.3.3缺省参数
缺省参数也叫默认参数,用于定义函数,为参数提供默认值,调用函数时可不传该默认参数的值(注意:所有位置参数必须出现在默认参数前,包括函数定义和调用)
def add(a, b=10):
return a+b
print(add(5))
print(add(5,7))
# 执行结果
15
12
函数调用时,如果为缺省参数传值则修改默认参数值, 否则使用这个默认值,上例第一次使用函数只传参a=5,b没有传则使用默认值10,第二次使用函数传了两个参数,a=5、b=7,因为传了b参数,所以不会使用默认值
5.3.4不定长参数
不定长参数也叫可变参数. 用于不确定调用的时候会传递多少个参数(不传参也可以)的场景.
不定长参数的类型: ①位置传递 ②关键字传递
位置传递
def info(*args):
print(args)
info("软件测试")
info("软件测试",1)
# 执行结果
# ("软件测试",)
# ("软件测试", 1)
传进的所有参数都会被args变量收集,它会根据传进参数的位置合并为一个元组(tuple),args是元组类型,这就是位置传递
关键字传递
def info(**kwargs):
print(kwargs)
info(name = "软件测试", age = 1)
# 执行结果
# {'name': '软件测试', 'age': 1}
参数是“键=值”形式的形式的情况下, 所有的“键=值”都会被kwargs接受, 同时会根据“键=值”组成字典
5.3.5函数作为参数
def sum(add):
res = add(1, 2)
print(res)
def add(a, b):
return a+b
sum(add)
# 执行结果
# 3
调用一个方法,将一个方法当成参数传过去
5.4模块
模块(Module),是一个 Python 文件,以 .py 结尾. 模块能定义函数,类和变量,模块里也能包含可执行的代码
模块的导入方式:
常用的组合形式:
import 模块名
from 模块名 import 类、变量、方法等
from 模块名 import *
import 模块名 as 别名
from 模块名 import 功能名 as 别名
自定义模块:
每个Python文件都可以作为一个模块,模块的名字就是文件的名字
6.异常处理
'''
以下是捕捉全部异常
'''
try:
# 代码块
except :
# 捕捉异常后执行
finally:
# 无论是否捕捉到异常都会执行
'''
以下是捕捉指定异常
'''
try:
# 代码块
except ZeroDivisionError:
# 捕捉指定的异常后执行
finally:
# 无论是否捕捉到异常都会执行
7.文件操作
在 Python 中,文件操作可以使用内置的 open() 函数。open() 函数接受一个文件名和一种模式,该模式指示如何处理文件。常见的模式包括读取模式、写入模式和追加模式。读取模式用于读取文件内容,写入模式用于写入文件内容,追加模式用于在文件末尾添加内容。
pythonCopy code# 打开文件
# 这里使用r模式
file = open("example.txt", "r")
# 读取文件内容并打印到控制台
print(file.read())
# 关闭文件
file.close()
一般使用with open使用,可以免除关闭文件操作
# 以下是读取csv文件操作
import csv
# 使用with open打开文件,这里的f变量代表你打开的文件数据,可以自定义
with open(filename,encoding="utf-8") as f:
file = csv.reader(f)
data = []
for i in file:
data.append(i)
return data
8.Python的数据类型
Python中的数据类型主要有:Number(数字)、Boolean(布尔)、String(字符串)、List(列表)、Tuple(元组)、Dictionary(字典)、Set(集合)。
其中又分为可变数据类型和不可变数据类型,可变数据类型是指可以随着函数的执行发生变化,而不可变数据类型不可以改变 不可变数据类型(3个):Number(数字)、Boolean(布尔)、String(字符串)、Tuple(元组) 可变数据类型(4个):List(列表)、Dictionary(字典)、Set(集合)
问:如何判断一个变量的类型?答:1.可以使用type(变量名称)方法,返回变量的数据类型;2.isinstance(变量名称,数据类型),只能返回True或False
8.1数字类型(Number)
Python3中有3种数据类型:int(整型)、float(浮点型)、complex(复数)。
Python3的整型没有限制大小,可以当作Long类型来使用,且bool是整型的子类型。
# 整型int,浮点型float
a=2
b=2.6
print(a,b,type(a),type(b),isinstance(a,int))
# 输出结果为
# 2 2.6 <class 'int'> <class 'float'> True
8.2布尔类型(Boolean)
布尔类型是与逻辑相关的一种数据类型,且只有True和False两个值。(注意:布尔值可以相加,相加之后类型就会转换为int类型)
# 2.布尔型Boolean
c=True
d=False
d=c+d
print(type(c),type(d),d)
# 输出结果
# <class 'bool'> <class 'int'> 1
8.3字符串类型(Str)
在Python变量定义中,通过单引号和双引号引起来的值就是字符串str类型。
8.3.1字符串截取、拼接和复用
str1 = "hello,world!"
print(str1[::])
print(str1[2:5])
print(str1[2,6,2])
print(str1[-1:-4])
print(str1[-5:-2])
print(str1[-1:-5:-1])
# 输出结果
hello,world!
llo
lo
空
orl
!dlr
a = "我是王菜鸟"
b = ",你呢"
print(a[0],a[-1],a[:3],a[1,-1],a+b,a*2)
# 输出结果
我 鸟 我是王 是王菜 我是王菜鸟,你呢 我是王菜鸟我是王菜鸟
8.3.2字符串拼接
cs='测试'
print('软件' + cs)
#------------ 运行结果
# 软件测试
#
# print打印语句会自动换行,如果不想自动换行只需要设置end参数,如下
cs = '测试'
print('软件' + cs,end='')
#------------ 运行结果
# 软件测试
8.3.3字符串格式化
我们可以通过如下语法,完成字符串和变量的快速拼接
name = '软件测试'
age = 1
height = 1.7
print('我是:%s,今年:%d岁,身高:%f米' % (name, age, height) )
#------------ 运行结果
# 我是:软件测试,今年:1岁,身高:1.700000米
其中,% 表示占位符,且在无需使用变量进行数据存储的时候,可以直接格式化表达式(变量的位置放入表达式),简化代码
8.3.4格式化的精度控制
我们可以使用辅助符号"m.n"来控制数据的宽度和精度
m,控制宽度,要求是数字,如果设置的宽度小于数字自身,则不生效
.n,控制小数点精度,要求是数字,会进行小数的四舍五入
示例: %5d:表示将整数的宽度控制在5位,如数字11,就会变成:[空格][空格][空格]11,用三个空格补足宽度。
%5.2f:表示将宽度控制为5,将小数点精度设置为2 。小数点和小数部分也算入宽度计算。如,对11.345设置了%7.2f 后,结果是:[空格][空格]11.35。2个空格补足宽度,小数部分限制2位精度后,四舍五入为 .35
%.2f:表示不限制宽度,只设置小数点精度为2,如11.345设置%.2f后,结果是11.35
# 还是使用上方代码,控制显示它身高精度
name = '软件测试'
age = 1
height = 1.7
print('我是:%s,今年:%d岁,身高:%.2f米' % (name, age, height) )
#------------ 运行结果
# 我是:软件测试,今年:1岁,身高:1.7米
8.3.5字符串快速格式化
通过语法:f"内容{变量}"的格式来快速格式化
# 还是使用上方代码
name = '软件测试'
age = 1
height = 1.7
print(f'我是:{name},今年:{age},身高:{height}米')
#------------ 运行结果
# 我是:软件测试,今年:1,身高:1.7米
注意:这种写法不做精度控制,不理会类型
8.3.6数据输入
使用input()语句可以从键盘中获取用户输入,如Java的Scanner
name = input("请输入你的名字:")
print(name)
注意:无论键盘输入什么类型的数据,获取到的数据永远都是字符串类型
8.4列表类型(List)
列表是经常用到的数据类型,其元素可以是字符串、数字、列表、元组等自定义的对象。列表是以[]定义,用英文逗号分隔开的集合,元素是可以重复的。列表中的操作通常包含索引、切片、in和not in、len、操作符+和*、添加/删除/查询元素、排序和反转、将元组、字符串等序列转换成列表(不会修改原有的序列)。
8.4.1 in和not in区别
判断某个值是否存在于列表中,若存在则返回True,否则返回False(注意:存在是指和列表的某个元素相同)。
store = [[65, 33], '女', 'wang', 24, 2.3]
print("wang" in store)
print("33" in store)
print("女" not in store)
# 输出结果
True
False
False
8.4.2 len() 方法
跟Java的length差不多,返回列表元素数量
store = [[65, 33], '女', 'wang', 24, 2.3]
print(len(store))
# 输出结果
5
8.4.3 添加、删除、查询元素
添加
# append
store = [[65, 33], '女', 'wang', 24, 2.3]
store.append("王菜鸟")
print(store)
store.append([66, 68])
print(store)
# extend
store.extend(["Kevin",168])
print(store)
# insert
store.insert(2,999)
print(store)
# 输出结果
[[65, 33], '女', 'wang', 24, 2.3, '王菜鸟']
[[65, 33], '女', 'wang', 24, 2.3, '王菜鸟', [66, 68]]
[[65, 33], '女', 'wang', 24, 2.3, '王菜鸟', [66, 68], 'Kevin', 168]
[[65, 33], '女', 999, 'wang', 24, 2.3, '王菜鸟', [66, 68], 'Kevin', 168]
删除
print(store.pop(2))
store.remove('Kevin')
print(store)
store.clear()
print(store)
# 输出结果
999
[]
[]
查询
store = [[65, 33], '女', 'wang', 24, 2.3,24]
print(store.index('女'))
print(store.count(24))
# 输出结果
1
2
8.4.4 排序(sort)和反转(reverse)
排序(sor)
格式:list.sort(key=None, reverse=False)
key – 指定带有一个参数并返回一个对象的函数,用于从列表中的元素提取需要比较的内容
sort()默认升序排序,即reverse=False,降序reverse=True
list1 = [22, 11, 25, 98, 72, 68, 49]
list1.sort()
print(list1)
list1.sort(reverse=True)
print(list1)
# 输出结果
[11, 22, 25, 49, 68, 72, 98]
[98, 72, 68, 49, 25, 22, 11]
注意:sort中key参数的用法
# 按照总分成绩降序排名
list1 = [[97, 54, 93], [59, 78, 83], [44, 64, 97], [83, 79, 73]]
def sum_scores(scores):
return scores[0] + scores[1] + scores[2]
list1.sort(key=sum_scores, reverse=True)
print(list1)
反转(reverse)
reverse:排序规则,reverse = True 降序, reverse = False 升序(默认)。
list1 = [22, 11, 25, 98, 72, 68, 49]
list1.reverse()
print(list1)
# 输出结果
[49, 68, 72, 98, 25, 11, 22]
8.4.5 list()
将元组、字符串等序列转成列表(不会修改原有的序列)。
# 元组
tup = (22, 86, 26, 35)
print(list(tup))
print(tup)
# 字符串
str1 = "Happy New Year"
print(list(str1))
print(str1)
# 输出结果
[22, 86, 26, 35]
(22, 86, 26, 35)
['H', 'a', 'p', 'p', 'y', ' ', 'N', 'e', 'w', ' ', 'Y', 'e', 'a', 'r']
Happy New Year
8.5 集合(set)
集合Set是一个无序且不可重复的序列,使用{}或set()函数进行创建,如果想创建一个空集合必须用 set() ,{ } 是用来创建一个空字典的。
注意:集合(Set)只存储不可变的数据类型,如Number(数字)、字符串、元组等,而无法存储列表、字典、集合这些可变的数据类型。
set1 = set({"Kevin", "Lucy", "Toke", 24}) print(set1) set2 = set("wang") print(set2) set3 = set({22, 33}) print(set3) # 输出结果 {'Toke', 24, 'Lucy', 'Kevin'} {'w', 'g', 'n', 'a'} {33, 22}
8.6 字典类型(Dictionary)
字典以键值对(key: value)的方式存储对象。
1、键(key)在字典中必须是唯一的,且键是不可变的数据类型,如字符串、数字、元组。如果创建时键被赋值多次,最后一个值才被存储。
2、值(value)可以是任意对象,比如None、数值、字符串、字典等。
dict1 = dict()
print(len(dict1), type(dict1))
dict2 = {}
print(len(dict2), type(dict2))
# 如果创建时键被赋值多次,最后一个值才被存储
dict3 = {"a": "Kevin", "b": "Lucy", "a": "Toke"}
print(dict3)
# 输出结果
0 <class 'dict'>
0 <class 'dict'>
{'a': 'Toke', 'b': 'Lucy'}
有Java基础就会知道,list和Java的array数组很像,而字典类型和Java的map集合很像,学会Java很容易学会Python
9.继承
Python继承和Java继承写法不同,但跟Java其他方面差不多,如继承父类的属性和方法等,Java中需要在类名后面写extend 而Python则直接在类名后面加上括号里面写需要继承的父类,如下所示:
# 父类
class Paren:
def show(self):
print('父类方法')
# 子类来继承父类
class Child(Paren):
def __init__(self):
super().__init__()
def display(self):
print('子类方法')
实操
使用Python语言和面向对象思想制作一个简易用户管理系统,实现增删改查功能,使用list当数据库
启动系统时,提示需要登录,现初始有一个账户为admin,密码为123456的用户
用户属性:编号、用户名、密码、年龄、身高
例:
请输入用户名:
请输入密码:
请判断用户名和密码是否正确,如果错误提示对应错误,否则显示以下内容:
**************
1、增加用户
2、删除用户
3、修改用户
4、查询用户
**************
欢迎使用用户管理系统,请选择您需要执行的操作:
当用户输入4时,则提示:
1、查询全部
2、通过编号查询
请选择您需要执行的操作:
实现以上功能,尽量解耦合
第三天:学会Selenium的基本使用
Selenium测试框架
简介:
Selenium是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。
安装Selenium(4.10.0)
安装Selenium框架两种方式:
使用Selenium之前还需要给浏览器安装驱动
谷歌浏览器为例:
先查看自己的浏览器谷歌版本
进入网站下载对应的版本,没有对应的就下载离版本最近的:http://npm.taobao.org/mirrors/chromedriver/(115之前) | https://googlechromelabs.github.io/chrome-for-testing/(115之后)
下载完后解压,把解压后的浏览器驱动放到与Python同级目录下
加载浏览器
driver = webdriver.Chrome()
注:
只要把driver设置为global,python执行完脚本时不会自动关闭
global driver
driver = webdriver.Chrome()
Selenium 的使用:
八大定位元素方法:
CLASS_NAME 通过class名称定位
CSS_SELECTOR 通过CSS选择器定位(不常用)
ID 通过ID定位
LINK_TEXT 通过a标签的文本定位
NAME 通过name定位
PARTIAL_LINK_TEXT 通过a标签的文本模糊定位(不常用)
TAG_NAME 通过标签名称定位
XPATH 通过xpath语法定位
元素使用方法
self.driver.find_element(By.ID,"ID"))
self.driver.find_element(By.CLASS_NAME,"CLASS_NAME"))
self.driver.find_element(By.CSS_SELECTOR,"CSS_SELECTOR"))
self.driver.find_element(By.LINK_TEXT,"LINK_TEXT"))
self.driver.find_element(By.NAME,"NAME"))
self.driver.find_element(By.PARTIAL_LINK_TEXT,"PARTIAL_LINK_TEXT"))
self.driver.find_element(By.TAG_NAME,"TAH_NAME"))
self.driver.find_element(By.XPATH,"XPATH"))
元素的常用信息
- 获取元素尺寸
- size
- 获取元素的宽度和高度
- 例:self.driver.find_element(By.ID,'kw').size
- 获取元素文本
- text
- 获取文本信息
- 例:self.driver.find_element(By.ID,'kw').text
- 获取元素属性值(某个属性)
- get_attribute(name)
- 获取标签内的属性信息
- 例:self.driver.find_element(By.ID,'kw').get_attribute(title)
- 获取kw的标题
- 判断元素是否可用
- is_enabled()
- 例:self.driver.find_element(By.ID,'kw').is_enabled()
- 判断元素是否可见
- is_displayed()
- 例:self.driver.find_element(By.ID,'kw').is_displayed()
- 判断元素是否被选中
- is_selected()
- 例:self.driver.find_element(By.ID,'kw').is_selected()
⭐元素常用操作
- 输入元素内容
- send_keys(*value)
- 往元素里输入内容
- 例:self.driver.find_element(By.ID,'kw').send_keys('百度')
- 清除元素内容
- clear()
- 清空元素里内容
- 例:self.driver.find_element(By.ID,'kw').clear()
- 提交表单
- submit()
- 提交页面中的form表单或者模拟按下 Enter 键提交表单
- 例:self.driver.find_element(By.ID,'kw').submit()
⭐鼠标的常用操作
需要先创建一个鼠标对象,ActionChains()
例:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
# 创建浏览器驱动
driver = webdriver.Chrome()
# 访问网页
driver.get('https://www.jd.com/')
# 定位手机
element = driver.find_element(By.XPATH,'//*[@id=\"J_cate\"]/ul/li[2]/a[1]')
# 创建鼠标对象
action = ActionChains(driver)
# 调用悬停的方法
aciton.move_to_element(element)
# 执行鼠标操作
action.perform()
# 14和16也可以和在一起做
# aciton.move_to_element(element).perform()
⭐浏览器的常用操作
浏览器窗口的设置
浏览器的前进与后退
- 浏览器前进
- driver.forward()
- 浏览器后退
- driver.back()
浏览器页面的刷新
- 刷新浏览器页面
- driver.refresh()
获取浏览器页面的标题和URL
- 获取浏览器页面的标题
- driver.title
- 获取浏览器页面的URL
- driver.current_url
浏览器的的关闭
- 关闭浏览器所有窗口
- driver.quit()
- 关闭浏览器当前窗口
- driver.close()
实操
使用selenium窗口最大化访问百度,搜索软件测试,最后关闭浏览器
http://xn--6frwj470ei1s2kl.com/demo 自动化测试练手网站
第四天:学会Selenium的高级用法
⭐Selenium的高级应用
下拉选择框操作
使用下拉框选择时,需要导入Select类
# 导入Select类
from selenium.webdriver.support.select import Select
# 并把元素转为Select,element为定位的元素
select = Select(element)
- 根据索引值定位指定选项
- select_by_index(index)
- 根据value值定位指定选项
- select_by_value(value)
- 根据文本定位指定选项
- select_by_visible_text(text)
弹出框操作
在进行弹出框操作时,需要获取Alert类对象
alert = driver.switch_to.alert
截图操作
- 获取页面截图,并将截图保存到指定路径下,filename为保存文件夹的路径
- get_screenshot_as_file(filename)
- 保存页面截图,该截图的后缀名为.png,filename为保存图片的名称
- save_screenshot(filename)
- 获取页面截图的base64编码字符串成
- get_screenshot_as_base64()
- 获取页面截图的二进制数据
- get_screenshot_as_png()
多窗口切换
- 获取当前窗口的句柄
- driver.current_window_handle
- 获取所有窗口的句柄
- driver.current_handles
- 切换到指定窗口
- driver.switch_to.window(handle)
# 使用多窗口切换时,一般先获取所有的窗口句柄,然后在切换
handles = self.driver.current_handles
self.driver.switch_to.window(handles[1])
多表单切换
switch_to.frame(frame_reference)
frame_reference可以是frame类型的标签中的name属性值或id属性值,也可以是frame标签在这个html中下标
需要注意的是,在定位多表单的元素时,首先需要调用default_content()方法返回到主页面,之后才能对另外一个frame类型的表单元素进行定位
self.driver.switch_to.frame(下标)
self.driver.switch_to.frame(id属性)
self.driver.switch_to.frame(name属性))
# 如果事先已经切换到一个frame表单里面,再想切换另一个frame时,需先回到主页面default_content(),再切换
self.driver.switch_to.defalut_content()
self.driver.switch_to.frame(1)
操作JavaScript
selenium操作js有两种方法
execute_script(同步执行)
# 使用selenium操作js添加属性
driver.execute_script("document.getElementById(‘xxx’).setAttribute(‘name’,‘xxx’)))")
# 使用selenium操作js移除属性
driver.execute_script("document.getElementById('xxx').removeAttribute('name')))")
execute_async_script(异步执行)
常用的js代码:
滚动条:element(元素).scrollIntoView(false|true) 显示到能看到该元素的位置
⭐ 元素等待
显示等待
使用前需要导入web DriverWait类
from selenium.webdriver.support.ui import WebDriverWait
WebDriverWait(driver,timeout,poll_frequency=POLL_FREQUENCY,ignored_exeptions=None)
WebDriverWait()方法中语法格式:
- dirver:必选参数,表示浏览器驱动对象
- timeout:必选参数,表示超时时间,即最长的显示等待时间,单位为秒
- poll_frequency:可选参数,表示查找指定元素间隔时间,单位为秒。该参数默认为常量POLL_FREQUENCY,该常量值为0.5,也就是查找指定元素的时间间隔默认为0.5秒
- ignored_exeptions:可选参数,表示可忽略的异常集合。当调用unitl()方法或unitl_not()方法时,如果程序抛出的异常是这个集合中的异常,则程序不会中断,会继续等待;如果抛出的是这个集合之外的异常,则程序会中断并抛出异常。在这个异常集合中默认只有NoSuchElementException异常
在进行显示等待时,WebDriverWait()方法必须与until()方法或until_not()方法结合使用
until(method,message='')
- method:必选参数,该参数是一个匿名参数,在该参数中调用了查找页面元素的方法。在规定的等待时间内,程序每隔一段时间会调用一次该匿名函数,直到该函数的返回值为Ture
- message:可选参数,表示超时后的异常信息,如果程序超时,则会抛出超时异常 TimeoutException,该参数的值会传递到TimeoutException()方法中
例:显示等待alert窗口
from selenium.webdriver.support import expected_conditions as EC
# 配合EC使用,表示显示等待alert窗口5秒,EC.alert_is_present()判断alert窗口是否出现
element = WebDriverWait(self.driver,5).until(EC.alert_is_present()))
EC模块
简介:
EC模块是Expected_Conditions缩写
导包:
from selenium.webdriver.support imoprt expected_conditions as Ec
常用方法:
- title_is(title)
- title_is(title) 判断页面title是否是指定文本(英文区分大小写),若完全相同则返回true,否则返回false
- title_contains(title)
- title_contains(title)判断页面title是否包含指定文本(英文区分大小写),若包含则返回true,否则返回false
- presence_of_element_located(locater)
- presence_of_element_located(locater)判断一个元素是否存在DOM树中,存在则返回元素本身,不存在则报错 locater为一个(By.XX)元组
- presence_of_all_element_located(locater)
- presence_of_all_element_located(locater)判断定位的元素范围内,至少有一个元素存在页面中,存在则以list形式返回元素本身,否则报错
- visibility_of_element_located(locater)
- visibility_of_element_located(locater)判断指定元素是否存在于DOM树中并且可见,可见意为元素的宽和高都大于0,存在则返回元素本身,否则返回false
- visibility_of(element)
- visibility_of(element)同上一样,参数变为一个元素,而不是一个元组
- invisibility_of_element_located(locater)
- invisibility_of_element_located(locater)判断元素是否隐藏
- text_to_be_present_in_element(locater,text)
- text_to_be_present_in_element(locater,text)判断给定文本是否出现在特定元素中,存在返回true,否则返回false
- text_to_be_present_in_element_value(locater,text)
- text_to_be_present_in_element_value(locater,text)判断文本是否存在特定元素的value值中,存在则返回true,否则返回false,对于没有value值的元素也会返回false
- alert_is_present
- alert_is_present判断alert是否存在,若存在则切换到aler,否则返回false
- element_to_be_selected(element)
- element_to_be_selected(element)判断元素是否被选中
隐式等待
implicitly_wait(timeout)
timeout表示隐式等待的最长时间
需要注意的是,隐式等待是全局设置,即在测试代码中只要设置了一次隐式等待,则该隐式等待会作用于页面中的所有元素
强制等待
sleep(seconds)
seconds表示程序想休眠时间
如果遇到前两个等待解决不了的问题,就用强制等待,不要听什么最好少用这个等待,只要遇到前两个等待解决不了就用
实操
https://hmshop-test.itheima.net/index.php,使用该网页实现用户登录后进行购买物品操作
用户名:13800000000,密码:123456
第五天:学会PO模型及用法
简介
PO(Page Object,页面对象),模式主要是将程序中的页面元素定位和元素操作封装成一个页面类,在该类中实现页面对象和测试用例的分离,该模式的核心是对页面元素的封装,从而减少程序中冗余代码,提高测试代码的可维护性和可读性。
总结:封装页面
使用
先创建一个基础(BasePage)类,里面包含对网页的基本操作,如点击、访问网页、获取文本等方法,然后创建对应的页面类,如首页类(IndexPage)等,里面包含对这个页面的操作,如点击这个页面的某个按钮,获取这个页面的某段文字。
1、创建BasePage基础类,将网页常用的操作封装到该类中
from selenium import webdriver
from selenium.webdriver.support.select import Select
class Basepage(object):
# 初始化时加载传入进来的浏览器
def __init__(self, driver):
self.driver = driver
# 访问网页
def get_url(self, url):
self.driver.get(url)
# 定位元素
def get_element(self, loc):
return self.driver.find_element(*loc)
# 定位元素输入
def get_input_text(self, text, loc):
self.get_clear(loc)
self.get_element(loc).send_keys(text)
# 点击元素
def get_click(self, loc):
self.get_element(loc).click()
# 清空元素
def get_clear(self, loc):
self.get_element(loc).clear()
2、创建对应的页面模型,使用BasePage类中的封装方法
from Website.test_report.BasePage import BasePage
from selenium.webdriver.common.by import By
# 创建页面模型并继承基础类
class LoginPage(BasePage):
# 初始化
def __init__(self, driver):
BasePage.__init__(self, driver)
'''
在方法中调用父类基础类中的方法
'''
# 输入手机号
def sent_phone(self, phone):
self.get_input_text(phone, (By.CLASS_NAME, "loginUsername"))
# 输入密码
def sent_pwd(self, pwd):
self.get_input_text(pwd, (By.NAME, "pwd"))
# 点击登录
def click_btn(self):
self.get_click((By.CLASS_NAME, "sublogin"))
3.调用对应的页面模型
import unittest
from selenium import webdriver
from selenium.webdriver.firefox.service import Service
from time import sleep
from login_page import LoginPage
from post_management_page import PostManagementPage
from add_post_page import AddPostPage
class WebCaseTest(unittest.TestCase):
# 浏览器初始化
def setUp(self):
self.driver = webdriver.Chrome()
# 设置隐式等待 5 秒
self.driver.implicitly_wait(5)
# 浏览器窗口最大化
self.driver.maximize_window()
# 填写被测站点地址
self.driver.get("https://664c5eceedab57b76fa0eb16.hz-iframe-svc.simplelab.cn/login")
# 请再此方法中续写测试用例代码
def test_browser(self):
# 创建LoginPage对象,供调用该类中的方法实现【登录】的操作代码
login_page = LoginPage(self.driver)
# 点击登录账号输入框并输入账户-admin
login_page.input_login_name("admin")
# 点击登录密码输入框并输入密码-LQ15Mock2@2004
login_page.input_login_password("admin123")
# 点击验证码输入框并输入-666
login_page.input_code("666")
# 点击登录按钮
login_page.click_login_button()
sleep(2)
# 创建PostManagementPage对象
post_management_page = PostManagementPage(self.driver)
# 创建PostManagementPage对象
add_post_page = AddPostPage(self.driver)
# TODO 请参照题目中【用例步骤】补全以下代码,调用 3 个Page类中的方法实现。
text = post_management_page.get_username_text()
self.assertEqual("蓝桥超管", text)
# 点击系统管理 -》 岗位管理
post_management_page.click_post_manage_path()
# 强制等待2秒,让页面加载
sleep(2)
# 切换frame,1代表页面中的第二个frame,0代表第一个
self.driver.switch_to.frame(1)
# 输入编码
post_management_page.input_post_code_search('ceo')
# 点击搜索按钮
post_management_page.click_search_button()
# 强制等待2秒,让数据展示
sleep(2)
# 获取信息进行断言
text = post_management_page.get_page_numberText()
# 断言
self.assertEqual("显示第 1 到第 1 条记录,总共 1 条记录", text)
# 点击删除按钮
post_management_page.click_delete_button()
# 等待1秒,让弹窗加载
sleep(1)
# 切换回默认frame
self.driver.switch_to.default_content()
# 点击确认删除按钮
post_management_page.click_ok_button()
sleep(1)
# 再切换
self.driver.switch_to.frame(1)
# 点击添加按钮
add_post_page.click_add_button()
# 等待2秒,让页面加载
sleep(2)
# 切换回默认窗口
self.driver.switch_to.default_content()
# 再执行切换,必须要先切换到默认窗口,再切换下一个,不然切换不了
self.driver.switch_to.frame(2)
# 输入内容
add_post_page.input_post_name('董事长')
add_post_page.input_post_code('ceo')
add_post_page.input_post_sort(1)
# 切换回默认frame
self.driver.switch_to.default_content()
# 点击确定
add_post_page.click_ok_button()
sleep(3)
# 浏览器退出
def tearDown(self):
if self.driver:
self.driver.quit()
if __name__ == "__main__":
unittest.main()
多动手,唯手熟尔
实操
https://hmshop-test.itheima.net/index.php,使用PO模式实现 该网页的购物操作
用户名:13800000000,密码:123456
第六天:学会Unittest框架及参数化
Unittest框架
简介
Unittest是Python的单元测试框架,它提供了一套丰富的测试工具和方法,包括测试用例、测试套件、断言、测试装置等。unittest还支持测试执行、测试报告和测试覆盖度等功能。使用unittest可以帮助开发者在开发过程中快速发现和修复代码中的问题,提高代码的质量和稳定性。
unittest的核心要素
- TestCase
- TestCase表示测试用例,测试用例是unittest框架中执行测试的最小单元,它通过unittest框架提供的断言方法来验证一组特定的输入操作或输入后得到的具体响应结果是否正确,在自动化测试程序中可以创建一个类来继承TestCase类,此时类中定义的每一个方法都是一个测试用例,这些测试方法必须以test开头
- TsetSuite
- TsetSuite表示测试套件,一个测试套件可以包含多条测试用例,测试套件的作用是将不同文件中的测试用例放在一个测试套件的对象中,这样一个测试套件就可以执行测试套件中存放的所有测试用例。在自动化测试中可以首先创建一个套件,然后调用addTest()方法将每条测试用例添加到测试套件的对象中。
- TextTestRunner
- TextTestRunner表示测试执行器,用于执行测试用例或测试套件并返回测试结果,TextTestRunner是运行测试用例的驱动类,该类中提供了run()方法来运行测试用例或测试套件
- TextTestResult
- TextTestResult表示测试结果,也称为测试报告,它用于展示所有测试用例执行成功或失败的结果信息。在测试程序中执行完测试用例或测试套件后,会将测试结果输出到控制台中,由于unittest框架中的测试结果显示的样式不美观,并且可读性差,所有通常会使用第三方插件HTMLTestRunner来展示测试用例的运行结果
- Fixture
- TsetLoader
- TestLoader类用于加载TestCase到TestSuite类的对象中,也就是说将想要运行测试用例封装到测试套件中。加载测试用例时需要调用TestLoader类的discover()方法,该方法用于自动搜索指定目录下指定开头的.py文件,并将这些文件中查找到的测试用例封装到测试套件中。
- 语法:discover(start_dir,pattern='test*.py'),start_dir表示要搜索的目录,pattern的值是一个通配符,也就是搜索以 * 前面字母为开头的.py文件。discover()方法返回值是TestSuite类对象
⭐unittest执行先后顺序
1.setUpClass() 在类运行之前执行
2.setUp() 在方法运行之前执行
3.test_xx() 测试用例
4.tearDown() 在方法运行结束后执行
5.tearDownClass() 在类执行结束后执行
⭐unittest断言
由于这些断言方法已经在unittest.TestCase类中定义了,自定义测试类时会继承TestCase类,所以在测试方法中可以直接通过self调用这些方法
⭐unittest用法
TestSuite(套件)用法
import unittest
from com.lanqiao.user_service import UserService
from com.lanqiao.user import User
class UserServiceTest(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.user_service = UserService()
def test_login(self) -> None:
# TODO: 请填写test1Login方法的单元测试代码
self.assertFalse(self.user_service.login("user1", "123456"))
self.assertFalse(self.user_service.login("name", "password1"))
self.assertTrue(self.user_service.login("user1", "password1"))
def test_update_password(self) -> None:
# TODO: 请填写updatePassword方法的单元测试代码
self.assertEqual(None, self.user_service.update_password("name", "123456"))
self.assertEqual("zhangsan", self.user_service.update_password("user1", "123456").name)
def test_update_password_exception(self) -> None:
# TODO: 请填写updatePassword方法的单元测试异常代码
self.assertRaises(ValueError, self.user_service.update_password,None, None)
@classmethod
def tearDownClass(cls) -> None:
cls.user_service = None
if __name__ == '__main__':
# TODO 请填写保证顺序执行test_login、test_update_password和test_update_password_exception方法的代码
# 创建套件(TestSuite)
suite = unittest.TestSuite()
# 将方法一个个加入套件中,运行顺序按照加入套件顺序执行
suite.addTest(UserServiceTest("test_login"))
suite.addTest(UserServiceTest("test_update_password"))
suite.addTest(UserServiceTest("test_update_password_exception"))
# 创建运行器(TextTestRunner)
runner = unittest.TextTestRunner()
runner.run(suite)
TestLoader(加载器)
# 创建加载器
loader = unittest.TestLoader()
加载器常用方法
⭐生成HTML测试报告
- 前置工作
1.首先下载HTMLTestRunner:http://tungwaiyip.info/software/HTMLTestRunner.html
2.将HTMLTestRunner.py文件放入自己的项目中
3.由于HTMLTestRunner是基于Python2开发的插件,而当前使用的是Python3,Python3的语法是在Python2的基础上发生了变更,如果想让HTMLTestRunner兼容Python3,就需要在HTMLTestRunner.py文件中做一些语法修订,修订内容如下。
from HTMLTestRunner import HTMLTestRunner
import unittest
# 把测试用例加载到测试组件中,使用TestsLoad类里面的discover方法
suite = unittest.TestLoad().discover(文件目录,测试用例文件)
# 打开文件,没有会自动创建一个report.html文件
file = open('repoert.html','wb')
# 实例化HTMLTestRunner,steam是要写入的文件流,title为标题,description为描述
runner = HTMLTestRunner(steam = file, title = '标题', description = '描述')
runner.run(suite)
file.close()
# 上述代码会在当前执行目录生成一个report.html测试报告文件
Unittest参数化测试
简介
使用Unittest参数化时,需学会ddt驱动,配合ddt驱动使用参数化
⭐数据驱动ddt
简介
DDT:Data Driver Tests
数据驱动ddt可以实现测试数据与测试脚本的分离,通过ddt来将测试数据加载到脚本中。采用数据驱动设计模式使一组数据对应一个测试用例,通过数据的改变从而驱动自动化测试的执行。既能减少代码量,也能降低代码的维护成本。
装饰器
通常情况下,data中的数据按照一个参数传递给测试用例,如果data中含有多个数据,如元组、列表、字典等数据,需要自行在脚本中对数据进行分解或者使用unpack分解数据
@ddt 类装饰器,用来标记当前类使用ddt数据驱动
@data 函数装饰器,用来给函数传递数据 @data(*fun)
@unpack 函数装饰器,用来对传递的数据进行解包,如解列表、元组、字典
@file_data 函数装饰器,用来直接读取yaml,json格式的文件数据,读取获取的数据是字典列表
常用用法
1、先写一个获取文件数据的方法,如下
import csv
def get_data(filename):
with open(filename, encoding="utf-8") as f:
lst = csv.reader(f)
data = []
for i in lst:
data.append(i)
return data
2、在类中使用ddt驱动
import unittest
from ddt import ddt,data,unpack
from EPR_PO.Website.test_case.model.myunit import MyTestCase
from EPR_PO.Website.test_report.AddPage import Addpage
from EPR_PO.Website.test_report.LogingPage import Loginpage
from EPR_PO.Website.test_case.model import function
from EPR_PO.Website.test_report.ReigisterPage import Registerpage
from EPR_PO.Website.test_report.BasePage import Basepage
# 使用ddt注解
@ddt
# MyTestCase为之前的unittest的TestCase,只不过这是自定类
class TestCase(MyTestCase):
def test_01(self):
# 把方法启动前创建好的self.driver传入进去
login = Loginpage(self.driver)
# 保存截图
function.get_screenshot(self.driver, self.screenshot, "登录")
# 断言
self.assertIn(login.Login("AutoTest", "123456"), "Welcome")
# ddt的data注解,使用上方写的读取数据方法
@data(*get_data(test_csv))
# 解包
@unpack
def test_02(self, value, keys):
# 要购买前需要登录
login = Loginpage(self.driver)
login.Login("AutoTest", "123456")
# 登陆后购买
add = Addpage(self.driver)
# 保存截图
function.get_screenshot(self.driver, self.screenshot, "购买")
# 断言
self.assertIn(add.Add(), keys)
@data(*get_data(reg_csv))
@unpack
def test_03(self, user, pwd):
# 注册
reg = Registerpage(self.driver)
reg.reg(user, pwd, self.reg_url)
function.get_screenshot(self.driver, self.screenshot, "注册")
if __name__ == '__main__':
unittest.main()
实操
1. 新建LQ_PO文件夹;
2. 在LQ_PO文件夹下新建driver文件夹和Website文件夹;
3. 在driver文件夹下新建driver.py文件存放浏览器驱动;
4. 在Website文件夹下新建test_case文件夹、test_report文件夹、test_data文件夹;
5. 在test_case文件夹下新建model文件夹和page_object文件夹;
6. 在model文件夹下新建function.py文件封装截图、数据驱动读取等方法;
7. 在model文件夹下新建myunit.py文件封装unittest框架中的Setup(添加5秒的智能等待和浏览器窗口最大化)和tearDown(退出浏览器)等方法;
8. 在page_object文件夹下新建BasePage.py文件封装selenium的基础操作类方法(get、find_element等);
9. 在page_object文件夹下新建LoginPage.py文件,引入BasePage.py中封装好的方法,引入By方法类,封装登录用例页面元素位置和操作(使用XPATH方法封装用户名输入框,使用XPATH方法封装密码输入框、使用XPATH方法封装登录按钮位置,封装输入用户名、密码、点击登录按钮等操作);
10. 在page_object文件夹下新建FreightPage.py文件,引入BasePage.py中封装好的方法,引入By方法类,封装好进入运费模板页面添加元素(使用XPATH方法封装门店管理菜单按钮,使用XPATH方法封装运费模板菜单按钮,使用XPATH方法封装新增按钮;
11.在page_object文件夹下新建AddFreightPage.py文件,引入BasePage.py中封装好的方法,使用XPATH封装模板名称输入框,使用XPATH封装输入首件(个)输入框,使用XPATH封装输入运费(元)输入框,使用XPATH封装输入续件(个)输入框,使用XPATH封装输入续费(元)输入框,使用XPATH封装确定按钮。
12. 在test_report文件夹新建srceenshot文件夹存放测试截图;
13. 在test_data文件夹新建test_csv.csv文件存放测试数据;
14. 在test_report文件夹下新建test_add.py文件,引入unittest、ddt以及之前封装好的model,LoginPage,FreightPage、AddFreightPage中的方法类,根据用例编写6条测试用例脚本,创建data参数来接收测试数据,并使用数据驱动输入用户名和密码(admin/LQSTesting@2004),使用数据驱动输入运费模板,然后进行截图操作,最后对每一条测试用例进行assertIn断言操作,对比提示信息是否和预期一致;
15. 在Website文件夹下新建run_test.py文件使用discover方法执行test_add.py测试用例并引入HTMLTestRunner方法生成html测试报告(测试报告title名为:Test Report,description内容为LanQiao test)。
第七天:实战自动化
https://cal.supfree.net/cal.html
https://parabank.parasoft.com/parabank/register.htm
http://hmshop-test.itheima.net/Home/user/login.html
使用上方给的网站,实战吧!!!熟能生巧,唯手熟尔