【python 学习知识整理】

目录

说明

python、selenium、pycharm基础知识

python

数据结构

(1)字符串。用''创建

列表list。用[]或list()创建

字典dict。用{}创建字典

集合set。用set()创建

关于None

关于=,copy和deepcopy

代码结构

1.3 获取当前时间

1.4 时间、时间戳转换

1.5 中文编码处理

1.6 字典&字符串转换

1.7 数据反射

1.9 数值区间

1.11 可变参数

1.12 继承、组合

1.13关于执行速度

selenium

xpath选择器---基础知识点,简单应用,布置任务

WebDriverWait、implicitly_wait、sleep

元素操作

查找元素

点击

输入

移动

其他

pycharm

部分快捷键、安装快捷键提示插件

调试

3.3 pycharm使用方法

unittest&pytest

4.1 用例名称命名

4.2 setUp和tearDown

5.  yaml文件

6.http常用请求方式

6.1 GET

6.2 HEAD

6.3 POST

6.4 PUT

6.5 PATCH

6.6 DELETE

6.7 OPTIONS

6.8 TRACE

6.9 CONNECT

7.pyautogui

8.RSA加密

关于密钥

加解密

9.cmd执行命令

框架相关知识

unittest

简介

本模块直接运行测试

自定义测试套件之addTest

自定义测试套件之discover

Unittest vs Pytest

四、安装依赖包问题

五、常见问题

JSONDecodeError

UnicodeEncodeError: 'latin-1' codec can't encode character '\u7b2c'

用例不运行

用例顺序不对

字典键错误、类型错误、list取值错误


说明

本文是平时进行自动化测试时学习与工作时的知识整理。如有不对之处请帮忙指正。

python、selenium、pycharm基础知识

  1. python

先谈谈对象、变量、常量、名字

通俗地说,对象它就是一个东西,它装的是数据。它有类型(比如bool型、整型int、浮点型float、字符串、列表、元组、字典,还有复数型),而它的类型决定了它装的数据是变量还是常量。变量是可变的,常量是不可变的。目前我们要用到的是变量。

我们可以改变已存在对象的数据,但是我们不能改变这个对象的类型。

变量也只是一个称呼,是在程序中为了方便引用内存中的值而为它取的名称。

变量名只能包含以下字符:a-z,A-Z,0-9,_

[例]

a=1

b=2

c=a

c=b

用type()方法可以知道一个对象的类型。例如type(a),结果会是<class 'int'>,表明这是一个int类型。

(class是对象的定义,在python中类和类型一般不加区分)

    1. 数据结构

数据结构及相关基本方法

(1)字符串。用''创建

基本方法

释义

举例

str()

可将其他类型转换为字符串

例如str(1),print(type(str1))

结果是<class 'str'>

\

转义。有一些常用的转义符:\n,\t,\',\"

\n表示换行、\t表示tab制表符

r

表示非转义的原生字符串

+

拼接

letters=‘abc’+'def',结果为abcdef

[]

提取。从左到右,第一个字符的偏移量为0,以此类推;

从右到左,最后一个字符的偏移量为-1,以此类推。

letters[0]的结果是字符a,

letters[-1]的结果是字符f

[start:end:step]

分片。偏移量从0开始。

[:]从头到尾

[start:]从start到结尾

[:end]从开头到end-1

[start:end:step]从start到end-1,每step个字符提取一个

number='0123456789'

number[0:10:2]

输出结果为:

02468

len()

获取长度

len(number)的结果是10

split()

分割。分割后的结果是一个列表。

s1='I have a new computer'

sp=s1.split(' ')

sp的结果为:

['I', 'have', 'a', 'new', 'computer']

类型为:<class 'list'>

join()

x.join(list)将列表list以字符串x进行合并

nowDate='2020-11-23'

nowDateSp=nowDate.split('-')

nowDateJ='-'.join(nowDateSp)

nowDateJ结果为:2020-11-23

列表list。用[]或list()创建

基本方法

释义

举例

list()

可将其他类型转换为列表

li=list('test')

结果为['t','e','s','t']

[]

可通过[偏移量]提取或修改元素。

从左到右,第一个字符的偏移量为0,以此类推;

从右到左,最后一个字符的偏移量为-1,以此类推。

letters=['I\'ll be waiting you','though I know you will never come back']

print(letters[1])

letters[1]='untill you come back'

print(letters)

输出结果为:

though I know you will never come back

["I'll be waiting you", 'untill you come back']

[start:end:step]

分片。偏移量从0开始。

[:]从头到尾

[start:]从start到结尾

[:end]从开头到end-1

[start:end:step]从start到end-1,每step个字符提取一个

append()

添加元素至尾部

letters=['I\'ll be waiting you,','untill you come back.']

letters.append('Please don\'t leave me alone')

print(letters)

输出结果为:["I'll be waiting you,", 'untill you come back.', "Please don't leave me alone"]

extend()或+=

合并列表。

insert(location,string)

在指定位置插入元素

del

删除指定位置的元素

remove()

删除具有指定值的元素

pop()

获取并删除指定位置的元素

index()

查询具有指定值的元素位置

count()

记录指定值出现的次数

sort(),sorted()

重新排列元素。

可添加参数reverse.reverse=True改变为降序排列。

listA.sort()会改变原listA的内容

sorted(listA)是创建了一个副本,不会改变原listA的内容

sort()对列表,sorted()对所有可迭代对象

=,copy()

=赋值;copy()复制

字典dict。用{}创建字典

用{}将一系列键值对(key:value)以逗号分隔包裹起来,格式为{键1:值1,键2:值2}。

字典是无序的。

基本方法

释义

举例

dict()

可将包含双值子列表的列表转换为字典

[key]

可通过[key]获取、添加或修改元素。

从左到右,第一个字符的偏移量为0,以此类推;

从右到左,最后一个字符的偏移量为-1,以此类推。

update()

合并字典。

del

删除指定键的元素

clear()

删除所有元素

in

判断是否存在。存在返回True,不存在则返回False。

keys()

获取所有键

values()

获取所有值

items()

获取所有键值对

=,copy()

=赋值;copy()复制

  1. 集合set。用set()创建

集合是无序的、且内容不可重复的。

可使用{a,b,c}创建集合,但是只能用set()创建空集合。

基本方法

释义

举例

set()

可将其他类型转换为集合。

重复的值会被丢弃。

in

判断是否存在。存在返回True,不存在则返回False。

&,intersection()

取交集

|,union()

取并集

-,difference()

取差集

^,symmetric_difference()

取异或集

<=,issubset()

取子集

>=,issuperset()

取超集

>

取真超集

  1. 关于None

None表示空值,它是一个特殊 Python 对象, None的类型是NoneType。但是None不等于0、空字符串、空列表、空字典等。特别注意不要搞错None和空字符串''。

可以认为None是内存中不同于其他的一块内存空间,字符串、空列表等各自指向其他内存块,而且None是唯一的。

可以将None赋值给任何变量,也可以给None值变量赋值。

判断一个变量是否为None,用if 变量 is None。

  1. 关于=,copy和deepcopy

copy和deepcopy要用到copy模块。from copy import copy,deepcopy

a=b赋值,是将a、b两个名字指向同一个对象

a=copy(b)或a=b.copy(),是会再创建一个对象,且是a指向新的对象,b依然指向旧的对象。但是copy只是浅层复制,只会复制一层,a对象中引用的内存地址不会改变。

a=deepcopy(b),是会创建新的对象,且引用的内存中数据会再复制一份。a对象中引用的内存地址是新的。

当碰到大型数据结构时,用copy就容易出现问题,如果用copy出现问题则改用deepcopy。

详细对比分析请看:https://blog.csdn.net/fflush_stdin/article/details/126919208?spm=1001.2014.3001.5501

【练习】

1.获取到了3个字段的提示信息:info1='"文本1"未填写',info2='"单人力1"未填写',info3='"编号"重复',info4='"登录名"重复'。(其中“”中的是字段名,未填写是该字段的提示内容)

  1. 分别用[]和split()获取字段名,并存入一个列表listName中

  2. 获取字段名和提示内容,以字段名为键、提示内容为字典创建一个字典dictInfo。

    1. 代码结构

  1. 注释、连接

基本方法

释义

举例

#

注释

\

放在末尾,表示连接下一行

A='lis'+\

't'

print(A)的结果为list

  1. if、elif、else;True、False

除了以下的假值以为,都会被认为是真值True

假值(False)

类型

布尔

False

null类型

None

整形

0

浮点型

0.0

空字符串

''

空列表

[]

空字典

{}

空集合

set()

  1. while、for循环,in,range(),break,continue

range()生成自然数序列

while

循环

i=1

while i<4:

print('我还在循环')

i+=1

for

循环

for i in range(1,4):

print(i)

break

跳出循环

for i in range(1, 5):

if i==3:

break

print(i)

i += 1

continue

跳出本次循环,进入下一次迭代开始

for i in range(1, 5):

if i==3:

continue

print(i)

i += 1

  1. 函数

如果有些代码要被复用,可用函数组织这些代码。

  1. 定义函数

def functionname(arg1,arg2,arg3=True,*args):

pass

args3=True是给这个参数赋一个默认值,如果调用函数时不传入该参数的值,那该参数就采用该默认值

*args将一组可变数量的位置参数集合成参数值的元组

  1. 调用函数

使用位置参数:functionname(arg1_value,arg2_value)

关键字参数:functionname(arg1=arg1_value,arg2=arg2_value)

  1. try、except

sts=['Something']

try:

sts.append('like')

print(sts)

except AttributeError as atr:

print(atr)

  1. 模块调用、对象、类、继承、覆盖

1)调用。需先导入,才能调用

import A:导入模块A

from A import function1:调用模块A中的方法

2)类、对象

class className1():

def functionName1(self):#self参数指向了正在被创建对象的本身

print("这是一个方法")

print('这是一个类')

className_1=className1()

className_1.functionName1()

class className1():

def __init__(self,name):#__init__根据类的定义创建实例对象,self参数指向了正在被创建对象的本身

self.name=name

def functionName1(self):#self参数指向了正在被创建对象的本身

print("这是一个方法")

print(self.name)

print('这是一个类')

className_1=className1('pfl')

className_1.functionName1()

  1. 继承

B类继承了A类,那么B类中就有了A类中的属性。可以通过B类的实例化对象直接调用A类中定义的属性。

class className1():

def __init__(self,name):#__init__根据类的定义创建实例对象,self参数指向了正在被创建对象的本身

self.name=name

def functionName1(self):#self参数指向了正在被创建对象的本身

print("这是一个方法")

print(self.name)

print('这是一个类')

class class_empty(className1):

pass

class_empty_1=class_empty('pfl')

class_empty_1.functionName1()

  1. 覆盖(override)

class className1():

def __init__(self,name):#__init__根据类的定义创建实例对象,self参数指向了正在被创建对象的本身

self.name=name

def functionName1(self):#self参数指向了正在被创建对象的本身

print("这是一个方法")

print(self.name)

print('这是一个类')

class class_empty(className1):

def functionName1(self):

print('这是子类覆盖的方法')

class_empty_1=class_empty('pfl')

class_empty_1.functionName1()

className_1=className1('pfl')

className_1.functionName1()

  1. 格式化

%s:字符串

%d:十进制整数

%f:十进制浮点数

{}和format():‘{顺序值}’.format(arg),如果有多个参数,可以根据顺序值决定插入的位置。

a='you'

b='me'

c='him'

print('%s and %s talk about %s'%(a,b,c))

print('{2} and {1} talk about {0}'.format(c,b,a))

  1. 正则表达式匹配(后续讲断言的时候再细讲)

需要用到re模块。

1)匹配

match(pattern,source):在source中匹配pattern,需要source以pattern为开始

compile(pattern):对于复杂的匹配,可用compile先对pattern进行编译以加快匹配速度

search():匹配source中任意一位置的pattern,返回第一次成功的匹配

findall():如果source中存在pattern,会返回所有不重叠的pattern,返回结果是一个包含所有匹配的元组的列表

sub(pattern,replacement,source):将source中的pattern替换为replacement。会新创建一个对象,不改变原source。

group(),groups():使用match()和search()匹配时,会以group()形式返回。用group()获取第一个匹配值,使用groups()获取所有匹配值。group(0)是所有,group(1)是拿到第一组。

str1='you\'re a pretty girl,so that I miss you everday.'

str2='Would you like to travel around the world with me? '

m1=re.match('you',str1)

m2=re.match('you',str2)

print(m1.group())

if m2:

print(m2.group())

else:

print('str2中未match到you')

m3=re.search('you',str2)

print(m3.group())

#compile

youPattern=re.compile('you')

m4=youPattern.search(str2)

print(m4.group())

#findall

m5=youPattern.findall(str1+str2)

print(m5)

#sub

m6=re.sub('I','he',str1+str2)

print(m6)

2)模式

特殊字符

模式

匹配

\d

一个数字字符

\D

一个非数字字符

\w

一个字母或数字字符、下划线

\W

一个非字母非数字字符、非下划线

\s

空白符

\S

非空白符

模式标识符

模式

匹配

abc

文本值abc

expr1|expr2

expr1或expr2

.

除\n外的任何字符

^

源字符串的开头

$

源字符串的结尾

prev?

0个或多个prev

prev*

0个或多个prev,尽可能多地匹配

prev*?

0个或多个prev,尽可能少地匹配

prev+

1个或多个prev,尽可能多地匹配

prev+?

1个或多个prev,尽可能少地匹配

prev{m}

m个连续的prev

prev{m,n}

m到n个连续的prev

[abc]

a或b或c

[^abc]

非a且非b且非c

[例]

str3='我在一个叫"Madrid"的地方,来了1天了'

pattern=re.compile('[^0-9a-z]+')

m=pattern.findall(str3)

print(m)

【练习】

1.获取到了3个字段的提示信息:info1='"文本1"未填写',info2='"单人力1"未填写'。(其中“”中的是字段名,字段名除数字外的是字段类型,未填写是该字段的提示内容)

  1. 获取字段类型和提示内容,以字段类型为键、提示内容为字典创建一个字典dictInfo。

参考:

匹配以分组的形式:p=re.compile(r'"(.*?)(\d+)"(.*)')

p=re.compile(r'"(\D+)')

匹配不以分组的形式:p=re.compile('[^"0-9]+')

1.3 获取当前时间

from datetime import datetime

datetime.now()

#得到的数据格式为:'2021-05-26 17:22:000000'

1.4 时间、时间戳转换

时间格式化为字符串(以datetime.now()为例):

time_str=datetime.now().strftime('%Y-%m-%d% H:%M:%S')

字符串转换为时间戳:time_s=int(time.mktime(time.strptime(startData, "%Y-%m-%d %H:%M:%S"))) #单位是秒

time_ms=time_s*1000 #单位是毫秒

时间戳转换为时间:time.localtime(float(time_ms/1000)))

日期时间直接转为时间戳:datetime.now().timestamp() * 1000

1.4.1 时间比较

#方式一:datetime类型直接相减

#获取当前年份总天数,以及总秒数

today=datetime.datetime.today()#获取当前时间

sum=0

for i in range(1,13):

day1, ndays=calendar.monthrange(today.year,i)

sum+=ndays

year_seconds =sum*24*60*60

#计算时间差

res_entryTime=1620316800000#从接口获取到的时间戳

entry_time = datetime.datetime.fromtimestamp(float(res_entryTime / 1000))# 将时间戳转成datetime类型

now_time = datetime.datetime.today()

long=now_time-entry_time#datetime类型,两个时间相减后的数据(timedelta类型)

long_times=long.days*24*60*60+long.seconds#相差的秒数

#方式二:时间戳直接相减

res_entryTime = 1620316800000#从接口获取到的时间戳

today = datetime.datetime.today()#获取当前时间

now_int=int(time.mktime(time.strptime(now_time.strftime("%Y-%m-%d %H:%M:%S"), "%Y-%m-%d %H:%M:%S")))*1000

star_int=int(time.mktime(time.strptime(str(today.year)+'-01-01 00:00:00', "%Y-%m-%d %H:%M:%S")))*1000

end_int=int(time.mktime(time.strptime(str(today.year+1)+'-01-01 00:00:00', "%Y-%m-%d %H:%M:%S")))*1000

year_int=end_int-star_int#当前年份一整年相差的时长(毫秒级)

long_int=now_int-res_entryTime#当前时间与res_entryTime相差的时长(毫秒级)

1.5 中文编码处理

xxx.encode('utf-8').decode('latin1')

1.6 字典&字符串转换

dictinfo = json.loads(json_str)输出dict类型

jsoninfo = json.dumps(dict)输出str类型

urlencode(dict) 字典转url表单键值对编码格式

1.7 数据反射

setattr(对象,属性名,属性值),设置属性值

应用场景:

用例1:新建日报,会产生一个唯一的blogid

用例2:编辑日报时需要编辑 用例1新建日报,可以在用例1 中进行参数blogid反射到类属性中

p = Person("laowang")

setattr(p,"talk",abc)   # 将abc函数添加到对象中p中,并命名为talk

p.talk(p)               # 调用talk方法,因为这是额外添加的方法,需手动传入对象p.talk(p)

1.9 数值区间

field1='gt'

field2='lgeq'

start='1'

end='3'

level=[]

li_1=['gt','gteq','lg','lgeq']

li_2=[False,True,False,True]

level.append(li_2[li_1.index(field1)])

level.append(float(start))

level.append(float(end))

level.append(li_2[li_1.index(field2)])

data=1

s=Interval(level[1],level[2],lower_closed=level[0],upper_closed=level[3])#创建数值区间

bl=data in s

1.11 可变参数

注意看下各项数据是否为同一个值,如果是,参数只要设置一个即可

接口方法参数不宜过多,如果数量过多则考虑命名可变参数(**kwargs)

*args:可称非键值可变参数、未命名可变参数

  • 传递一个非键值变长参数列表

  • 关键是*,不是args,args可替换成其他名称。

  • 解包赋值给位置参数。如果可变参数(args)之前有位置参数,解包参数会优先赋值给位置参数,剩余的变量赋值给可变参数

**kwargs

  • 传递关键字变长参数

  • 关键是*,不是args,args可替换成其他名称。

  • 解包赋值给关键字参数

  • 传递方式:

参数名=参数值(attrKey=value)

或**字典(**{attrKey:value})

1.12 继承、组合

  • 出现的原因:为了代码复用

  • 继承:类与类之间有共同的属性,那就可以把这些属性提取出来作为基类的属性,基类即成为父类,原来的类(亦可称派生类)继承该基类。其他特有属性可在子类中声明。子类中可重写父类方法。

电脑分为台式机和笔记本。台式机和笔记本都有主板、CPU、显卡、内存条、硬盘、风扇。

电脑成为父类,电脑的属性就有主板、CPU、显卡、内存条、硬盘、风扇

台式机为子类,继承电脑,也就继承了电脑的属性,也有主板、CPU、显卡、内存条、硬盘、风扇

笔记本为子类,继承电脑,也就继承了电脑的属性,也有主板、CPU、显卡、内存条、硬盘、风扇

但笔记本属性有差异。比如CPU的架构、接口、引脚有差异,故在笔记本类中可重写CPU。台式机同理。

  • 组合:在一个类中将另一个类的对象作为属性。

电脑由主板、CPU、显卡、内存条、硬盘、风扇组成。这些配件又分别有自己的元件,那么这些配件可分别成类。

主板类的属性有:BIOS芯片、I/O控制芯片、直流电源供电插件、指示灯插件、控制开关接口、扩充卡槽等。其他配件同理。

电脑类和主板类则是组合关系。

1.13关于执行速度

  • 方法参数不宜过多

  • for循环执行相对慢,尽量不要多层嵌套,如果需要多层嵌套,一层嵌套中每次迭代不互相影响的情况下可考虑加线程并行运行(速度会大大提高)

  • 字典、列表更新,尽量用内置函数。比如update、extend

  • 可了解下:update’, ‘字典构造器-关键字参数’, ‘chain’, ‘元素拼接’, ‘推导式’,'for循环', ‘ChainMap’

  • 可了解下:time.perf_counter()

  1. selenium

    1. xpath选择器---基础知识点,简单应用,布置任务

  1. 绝对路径选择、相对路径选择、指定标签名,通配符

绝对路径(/):从根节点开始的,到某个节点,每层都依次写下来,每层之间用 / 分隔的表达式

相对路径(//):不管在什么位置

指定标签名:所果要查找所有的div,可用//div

通配符(*):如果要查找所有的节点,不管什么类型,可用//*

  1. 根据属性选择、属性值包含字符串

[@属性名="属性值“]

[contains(@属性名,"属性值")]

*其中text()特殊

[text()="属性值“]

  1. (By.XPATH,'//input[@weid="f56o40_jom1so_i8bhs1_9w0hkp_ymnnrt_m2qt0r_sdj4yn_ay5iow_7lhf0u_98yaqr@0_htfv0m@0_nf78ld@0_zggflt_ud12pe@typeName"]')按次序选择

//ul/li[2]:所有的ul下层第2个li

(//ul/li)[2]:所有的ul下层li的第2个

  1. 组选择、父节点、兄弟节点

组选择(|):A|B,既有A的结果,又有B的结果

父节点(/..):A/..,A结果的上一层

后兄弟节点或者说弟弟节点(/following-sibling::),A/following-sibling::div,表示A的后兄弟结点且是div类型

前兄弟节点或者说哥哥节点(/preceding-sibling::)

    1. WebDriverWait、implicitly_wait、sleep

  1. sleep(睡眠)

from time import sleep

sleep(秒数)

直接睡眠秒数时长

  1. implicitly_wait(隐式等待)

driver.implicitly_wait(秒数)---》由于我们写代码时的框架,需写成:self.driver.implicitly_wait

若接下来要查找的元素未找到则等待,若已找到则程序往下继续执行,最大等待时长为设置的秒数

  1. WebDriverWait(显示等待)

wait=WebDriverWait(driver,秒数)---》由于我们写代码时的框架,需写成:wait=WebDriverWait(self.driver,秒数)

wait.until(expected_conditions.presence_of_element_located((By.XPATH,'具体的xpath')))

直到条件满足时停止等待,最长等待时长不超过设置的秒数。

    1. 元素操作

查找元素

By类定位元素方式

点击

element.click()

输入

element.send_keys('')

移动

action_chains.move_by_offset(x, y).click().perform()

    1. 其他

冻结窗口:setTimeout(function(){debugger},times)

times的单位为毫秒。执行该语句,在times时间后会冻结该窗口。

直接执行javascript:driver.execute_script(js)

  1. pycharm

    1. 部分快捷键、安装快捷键提示插件

复制一行代码到下一行:ctrl+D

排列代码格式:ctrl+alt+L

运行:shift+F10

调试:shift+F9

查找某个classes:ctrl+N

当前文件查的:ctrl+F

当前文件替换:ctrl+R

快速打开任何类、字段或函数:Ctrl+Alt+Shift+N

将两行合并为一行并删除不必要的空格:Ctrl+Shift+J

行注释或取消行注释:Ctrl + /

单步调试(一行一行走):F8

调试进入内部:F7

退出:Shift + F8

在当前行加上断点/断点开关:Ctrl + F8

查看所有断点:Ctrl + Shift + F8

    1. 调试

如何调试

异常名

释义

举例

NoSuchElementException

没有找到元素

NoSuchAttributeException

属性错误

lementNotVisibleException

元素不可见

NoSuchFrameException

没有找到iframe

NoSuchWindowException

没找到窗口句柄handle

NoAlertPresentException

没找到alert弹出框

ElementNotSelectableException

元素没有被选中

TimeoutException

查找元素超时

NameError

属性名错误,该属性名未定义

StaleElementRefrenceException

元素不存在或元素已过期

3.3 pycharm使用方法

3.3.1正则

选中表达式,按ctrl+enter

  1. unittest&pytest

4.1 用例名称命名

  1. 用例名称需以test_开头

单个运行:右键点击

(当用例名称以test_开头,才会识别为用例,右键菜单中会显示以unittest方式运行。非test_开头的则以纯python文件运行)

按规则匹配:运行runpytest.py

  1. 类名当以Test_开头时,不可设置__init__方法。

4.2 setUp和tearDown

setUpClass(cls)

类创建时执行,第一个要执行的用例执行之前执行。

方法前需加@classmethod以声明这是一个类方法。

tearDownClass(cls)

最后一个要执行的用例执行之后执行。

方法前需加@classmethod以声明这是一个类方法。

setUp(self)

每一个用例执行前执行。

tearDown(self)

每一个用例结束后执行。

5.  yaml文件

      读取文件:

        with open('yaml文件路径', encoding='utf-8') as fp_stream:

            '变量名' = yaml.load(fp_stream, Loader=yaml.FullLoader)

6.http常用请求方式

6.1 GET

用于使用给定的URI从给定服务器中检索信息,即从指定资源中请求数据。使用GET方法的请求应该只是检索数据,并且不应对数据产生其他影响。

6.2 HEAD

与GET方法相同,但没有响应体,仅传输状态行和标题部分。这对于恢复相应头部编写的元数据非常有用,而无需传输整个内容。

6.3 POST

用于将数据发送到服务器以创建或更新资源,它要求服务器确认请求中包含的内容作为由URI区分的Web资源的另一个下属。

POST请求永远不会被缓存,且对数据长度没有限制;我们无法从浏览器历史记录中查找到POST请求。

6.4 PUT

用于将数据发送到服务器以创建或更新资源,它可以用上传的内容替换目标资源中的所有当前内容。

它会将包含的元素放在所提供的URI下,如果URI指示的是当前资源,则会被改变。如果URI未指示当前资源,则服务器可以使用该URI创建资源

6.5 PATCH

是对PUT方法的补充,用来对已知资源进行局部更新

6.6 DELETE

用来删除指定的资源,它会删除URI给出的目标资源的所有当前内容

6.7 OPTIONS

用来描述了目标资源的通信选项,会返回服务器支持预定义URL的HTTP策略。

6.8 TRACE

用于沿着目标资源的路径执行消息环回测试;它回应收到的请求,以便客户可以看到中间服务器进行了哪些(假设任何)进度或增量。

6.9 CONNECT

用来建立到给定URI标识的服务器的隧道;它通过简单的TCP/IP隧道更改请求连接,通常使用解码的HTTP代理来进行SSL编码的通信(HTTPS)。

7.pyautogui

安装:pip install pyautogui

官方文档:https://pypi.org/project/PyAutoGUI/

我们目前常用:鼠标控制。坐标原点是屏幕最左上角的那个点。(selenium的坐标原点是网页最左上角的点)

position():获取当前鼠标位置。currentMouseX, currentMouseY = pyautogui.position()

moveTo(x,y,duration):移动到某个绝对位置。duration为执行此次动作设置时间,单位为秒。

moveRel(xOffset,yOffset,duration):相对于鼠标当前位置移动对应距离。

dragTo(x,y,button='left'):按下鼠标左键移动到某个绝对位置(也就是拖拽)。

dragRel (xOffset,yOffset,button='left'):按下鼠标左键相对于鼠标当前位置移动对应距离。

click(x,y,button):模拟点击(默认是左键)

实例:base.py中ele_dragRell()

8.RSA加密

关于密钥

对称加密:加密和解密时使用同一个密钥。

AES 、DES、3DES、Blowfish、IDEA、RC4、RC5、RC6 、

非对称加密:加密和解密时使用不同的密钥(公钥、私钥)。

RSA、ECC(移动设备用)、Diffie-Hellman、El Gamal、DSA(数字签名用)

公钥:公开的密钥

私钥:私有的密钥

加解密

.pem:PEM格式的文件

load_pkcs1_openssl_pem:加载符合PKCS标准的文件(.pem)

load_pkcs1_openssl_der:加载公钥原始二进制字节流

encrypt(arg1,arg2):arg1是待加密的数据,arg2是公钥

decrypt(arg1,arg2):arg1是待解密的数据,arg2是私钥

其他:

encode():编码。将str类型转为bytes类型

decode():解码。将 bytes 类型的二进制数据转换为 str 类型

9.cmd执行命令

格式:命令名 参数 参数

例如 cmd中执行命令 python runtest.py test_fna_AA_findAmountDigits test_fna_AB_saveAmountDigits

除了命令python外,其他都是参数,参数是以空格分隔的

sys.argv得到的是参数list。

sys.argv[0]是文件名runtest.py

sys.argv[1]是用例名test_fna_AA_findAmountDigits

  1. 框架相关知识

unittest

简介

单元测试框架,一个测试用例是一个独立的测试单元。

测试与报告框架互相独立。

部分:

test fixture(测试固定配置):本测试模块的配置,setUp_class,tearDown_class,setup,tearDown

test case(测试用例): unittest 提供一个基类: TestCase ,用于新建测试用例

test suite(测试套件):多个测试用例的集合。

test runner(测试运行器):一个用于执行和输出测试结果的组件。

本模块直接运行测试

class TestA(unittest.TestCase):

def test_A(self):

print('这是第一个测试用例')

def test_B(self):

print('这是第二个测试用例')

if __name__ == '__main__':

unittest.main()

unittest.main(): 执行用例。实际走的是testRunner.run()

自定义测试套件之addTest

class TestA(unittest.TestCase):

def test_A(self):

print('这是第三个测试用例')

def test_B(self):

print('这是第四个测试用例')

if __name__ == '__main__':

suite = unittest.TestSuite()

suite.addTest(TestA('test_A'))

suite.addTest(TestA('test_B'))

runner = unittest.TextTestRunner()

runner.run(suite)

unittest.TestSuite(): 生成一个测试套件

addTest(类名(方法名)):往测试套件里添加测试用例

自定义测试套件之discover

class TestA(unittest.TestCase):

def test_A(self):

print('这是test_suite_discover第一个测试用例')

def test_B(self):

print('这是test_suite_discover第二个测试用例')

if __name__ == '__main__':

path=os.path.join(os.getcwd())

all=unittest.defaultTestLoader.discover(path,pattern='test_suite_discover.py')

# all=unittest.defaultTestLoader.discover('./',pattern='test_suite_discover.py')

runner = unittest.TextTestRunner()

runner.run(all)

unittest.defaultTestLoader.discover(path,pattern): 自动发现并加载用例为一个测试套件

path:要运行的用例所在目录。可用绝对路径或相对路径

pattern:匹配模式

Unittest vs Pytest

pytest官方中文版:Pytest权威教程(官方教程翻译) - 韩志超 - 博客园

unittest

pytest

用例编写规则

1)测试文件必须先import unittest

2)测试类必须继承unittest.TestCase

3)测试方法必须以“test_”开头

4)测试类必须要有unittest.main()方法

1)测试文件名必须以“test_”开头或者"_test"结尾(如:test_ab.py)

2)测试方法必须以“test_”开头

3)测试类命名以"Test"开头

用例分类执行

默认执行全部用例,也可以通过加载testsuit,执行部分用例

可以通过@pytest.mark来标记类和方法,pytest.main加入参数("-m")可以只运行标记的类和方法

用例前置和后置

提供了setUp/tearDown,只能针对所有用例

pytest中的fixture显然更加灵活。可以任意自定义方法函数,只要加上@pytest.fixture()这个装饰器,那么被装饰的方法就可以被使用

参数化

需依赖ddt库

使用@pytest.mark.parametrize装饰器

断言

很多断言格式(assertEqual、assertIn、assertTrue、assertFalse)

只有assert一个表达式,用起来比较方便

报告

使用HTMLTestRunnerNew库

有pytest-HTML、allure插件

失败重跑

无此功能

pytest支持用例执行失败重跑,pytest-rerunfailures插件

2、Pytest全局用例共用之conftest.py详解

一、conftest特点:

1、可以跨.py文件调用,有多个.py文件调用时,可让conftest.py只调用了一次fixture,或调用多次fixture

2、conftest.py与运行的用例要在同一个pakage下,并且有__init__.py文件

3、不需要import导入 conftest.py,pytest用例会自动识别该文件,放到项目的根目录下就可以全局目录调用了,如果放到某个package下,那就在改package内有效,可有多个conftest.py

4、conftest.py配置脚本名称是固定的,不能改名称

5、conftest.py文件不能被其他文件导入

6、所有同目录测试文件运行前都会执行conftest.py文件

二、conftest用法:

conftest文件实际应用需要结合fixture来使用,fixture中参数scope也适用conftest中fixture的特性,这里再说明一下

1、fixture

fixture(scope='function',params=None,autouse=False,ids=None,name=None):

fixture里面有个scope参数可以控制fixture的作用范围,scope:有四个级别参数"function"(默认),"class","module","session

params:一个可选的参数列表,它将导致多个参数调用fixture功能和所有测试使用它。

autouse:如果True,则为所有测试激活fixture func可以看到它。如果为False则显示需要参考来激活fixture

ids:每个字符串id的列表,每个字符串对应于params这样他们就是测试ID的一部分。如果没有提供ID它们将从params自动生成

name:fixture的名称。这默认为装饰函数的名称。如果fixture在定义它的统一模块中使用,夹具的功能名称将被请求夹具的功能arg遮蔽,解决这个问题的一种方法时将装饰函数命令"fixture_<fixturename>"然后使用"@pytest.fixture(name='<fixturename>')"。

2、fixture的作用范围

fixture里面有个scope参数可以控制fixture的作用范围:session>module>class>function

-function:每一个函数或方法都会调用

-class:每一个类调用一次,一个类中可以有多个方法

-module:每一个.py文件调用一次,该文件内又有多个function和class

-session:是多个文件调用一次,可以跨.py文件调用,每个.py文件就是module

function默认模式@pytest.fixture(scope='function')或 @pytest.fixture()

3、conftest结合fixture的使用

conftest中fixture的scope参数为session,所有测试.py文件执行前执行一次

conftest中fixture的scope参数为module,每一个测试.py文件执行前都会执行一次conftest文件中的fixture

conftest中fixture的scope参数为class,每一个测试文件中的测试类执行前都会执行一次conftest文件中的fixture

conftest中fixture的scope参数为function,所有文件的测试用例执行前都会执行一次conftest文件中的fixture

三、conftest应用场景

1、每个接口需共用到的token

2、每个接口需共用到的测试用例数据

3、每个接口需共用到的配置信息

四、安装依赖包问题

1.无法安装上依赖包

重新安装pip:easy_install.exe pip

再重新安装依赖包

2.出现权限问题,检查path,用管理员运行

五、常见问题

  1. JSONDecodeError

原因:接口返回信息response有问题

处理:

  1. debug查看response返回数据 ,确认是否有'text'字段。

  2. 排查为什么response有问题

  1. 检查url(post请求时,注意要有eteamsid)

  2. 检查headers中的请求格式是否正确(Content-Type)

  3. 检查data(即payload)数据是否正确(格式、内容,引号嵌套是否正确)

  1. UnicodeEncodeError: 'latin-1' codec can't encode character '\u7b2c'

原因:请求时,数据存在中文

处理:payload.encode('utf-8').decode('latin1')

  1. 用例不运行

原因:用例名称未以test_开关

  1. 用例顺序不对

原因:用例运行根据用例文件名及用例名称,逐个字符判断大小,从小的开始运行

  1. 字典键错误、类型错误、list取值错误

排查:(1)检查response数据格式,与自己取值的格式是否一致

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值