Python编程语言与测试框架

Python环境管理
Menu: Python01环境变量 13page

一、安装
1.安装python ,Mac默认安装python2.7版本,如果下载的是python3版本,默认安装路径为 /usr/local/bin/python3.8
如果需要默认使用python3版本,需要创建软链接 ln -f /usr/local/bin/python3.8 /usr/python/bin/python 回车

2.Mac配置环境变量,环境变量路径 $PATH  ,查看环境变量是否进行了配置。如果没有配置需要将python安装路径 /usr/local/bin加入环境变量 

3.vim ~/.bash_profile 配置环境变量 export PATH=$PATH:/usr/local/bin  # $PATH表示在原来的环境变量基础上进行追加   export PATH=$PATH:需要添加的环境变量


二、pip是Python中的标准库管理器
sys、os库 自带
pip可以安装 python包自带外的其他包
pypi.org    # 网站里面包含很多库,可以上去搜索,同时也可以查到安装命令

pip help  	# 查看pip帮助文档
	install # 安装
	freeze  # 项目里面用到的依赖包,通过freeze命令全部导入到一个固定格式文件里,然后你可以将文件转交给其他人去部署相同的环境
	list    # 查看电脑上安装的所有文件包

部分外国网址下载包时耗时太长,我们可以使用国内的镜像源:
	阿里云:https://mirrors.aliyun.com/pypi/simple/
	清华:https://pypi.tuna.tsinghua.edu.cn/simple/
	豆瓣:http://pypi.douban.com/simple/

#CMD安装国外包:
pip install jupyter -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com

Menu: python基本数据类型与操作 8page

三、python基本数据类型与操作

Python官方文档(中文版)
Python经典书籍(进阶后看):《流程的Python》、《python cookbook》

变量、数字、字符串、列表

变量: 
	a = 1
	print(a)
	print(id(a))  #id可以 打印变量的存储地址

快捷注释:ctrl + /

数字:
	int
	float
    complex

字符串:
	\n			  #换行
	\        	  #转义符
	r			  #忽略转义符的作用
	+以及空格 	  #多个字符串连接,对变量进行连接不能使用空格来连接
	切片
切片举例:
str = "123456"
print(str[0])     #打印结果为1

#[开始:结束:步长]
print(str[0:5:2]) #打印结果为135

列表:
定义
list1 = [1,2,3,4,5]

索引
list1[0]		  #打印结果为1

切片
list1[0:5:2]	  #打印结果为[1,3,5]

list1[:]          #表示对列表进行复制 打印结果为[1,2,3,4,5]

Menu: Python控制流语法

Python分支结构
Python循环结构
	for-in循环
	
	range函数
		range(101)  	#产生一个0到100的整数序列
		range(1,100)	#产生一个1到99的整数序列
		range(1,100,2)	#产生一个1到99的奇数序列,2是步长

debug:选择需要定位的列打断点,点击右上角的小虫子,然后点击下一步下一步,慢慢去调试

	while循环
	break语句
	continue语句

Menu:Python的函数 11page

函数的定义、调用
	函数代码块以 def 关键词开头,后接函数名称和圆括号()
	冒号起始
	圆括号中定义参数
	
#函数的定义
def func(a,b,c):
    '''
    #连续按三下引号,键入enter键  函数说明-文档字符串
    :param a:
    :param b:
    :param c:
    :return:
    '''
    print("这是一个函数")
    print("这是一个参数a",a)

函数的调用形式
调用时的传参
	位置参数     #三个参数,按照位置传入
	
	
#函数的调用
func("a",1,2)

函数的各类参数
	1.默认参数	#默认参数在定义函数的时候使用k=v的形式定义
				#调用函数时,如果没有传递参数,则会使用默认参数

def func2(a=2):
	print("参数a的值为",a)

#函数调用	
func2()


	2.关键字参数 #在调用函数的时候,使用k=v的方式进行传参
				 #在函数调用/定义中,关键字参数必须在位置参数后面
			
	3.特殊参数   #

Lambda表达式(匿名函数) #可以用lambda关键字来创建一个小的匿名函数
					   #lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去
					   

#定义单个参数					   
func3 = lambda x: x*2

print(func3(2))  #调用

#定义多个参数
func4 = lambda x,y:x+y

print(func4(2,3)) 

Menu:Python常用数据结构

列表list
	list1.append(x)  			#在列表末尾添加一个元素
	list1.insert(i,x) 			#在给定的位置插入一个元素,第一个参数是要插入的元素索引
	list1.remove(x)				#根据元素值进行删除
	list1.pop(x)				#pop方法是有指定返回值的(区别于insert,remove等没有返回值),可以进行打印。删除列表中给定位置的元素并返回它。根据索引去删除
	
	#并非列表所有数据可以排序或比较(字符串和数字等)
	list1.sort()				#对列表中的元素升序排序 
	list1.sort(reverse=True) 	#对列表中的元素降序排序
	list1.reverse()				#对列表中的元素进行反转
	
列表推导式:提供了一个更简单创建列表的方法

'''
生成一个平方列表,比如[1,4,9],使用for循环怎么写,使用列表生成式怎么写
'''

#for循环
list_square1 = []
for i in range(1,4):
    list_square1.append(i**2)
print("list_square1",list_square1)

#列表推导式
list_square2 = [i **2 for i in range(1,4)]
print("list_square2",list_square2)     			#打印结果为[1,4,9]

#列表推导式也可以加入判断条件
list_square3 = [i **2 for i in range(1,4) if i != 1]
print("list_square3",list_square3) 				#打印结果为[4,9]


#嵌套循环
list_square4 = []

for i in range(1,4):
    for j in range(1,4):
        list_square4.append(i*j)
print(list_square4)   							#打印结果[1, 2, 3, 2, 4, 6, 3, 6, 9]
	
	
#列表推导式嵌套循环
list_square5 = [i * j for i in range(1,4) for j in range(1,4)]
print("list_square5",list_square5)				#打印结果[1, 2, 3, 2, 4, 6, 3, 6, 9]	
	

元组
元组使用()进行定义
tuple、list、range都是序列数据类型
元组是不可变的,可以通过解包、索引来访问


元组tuple
	count 				#计算元素出现的次数
	index				#计算元素的索引,如果该元素出现多次,以第一次为准

tuple_6 = (1,2,3,"a","a")
print(tuple_6.count("a"))       #打印结果为2
print(tuple_6.index("a"))       #打印结果为3


#元组定义
tuple_1 = (1,2,3)
print(tuple_1,type(tuple_1))

#元组不能像列表那样去赋值
#如果元组嵌套了列表,元组里面嵌套的列表是可变的

a = [1,2,3]
tuple_2 = (1,2,a)
print(id(tuple_2))      #查看元组的内存地址
#改变列表a的值
tuple_2[2][0] = "a"
print(id(tuple_2))      #查看元组的内存地址

print(tuple_2)          #打印结果为(1, 2, ['a', 2, 3])


集合set
	               
	remove				#移除
	union				#取多个集合结果的并集
	intersection		#取多个集合结果的交集
	difference			#多个集合,取出只有集合1里面才有,集合2里面没有的元素
	add					#添加元素
	
set1 = {1,2,3}
set1.add(4)
print(set1)                     		#打印结果为{1, 2, 3, 4}

#集合定义,可以用{}或者set进行定义,用{}不可以定义空集合
a = {1}  
b = set()  								#定义空集合

#集合也可以使用列表推导式
print({i for i in "asssdaghahgsdjg"})	#打印结果为{'s', 'j', 'a', 'd', 'g', 'h'}


#集合是默认去重的
result = "i am a dog"
print(set(result))             			#打印结果为{' ', 'g', 'o', 'd', 'a', 'm', 'i'}


字典dict
	keys 								#打印所有的key值
	values								#打印所有的value值
	popitem								#随机选取一对键值对进行删除,并返回被删除的键值对
	pop									#删除键值对,输入删除的key值,返回结果为其value值
	fromkeys							#将列表或者元组里面的值作为key值,转换为字典


list_3 = [1,2,3]
dict3 = {}
print(dict3.fromkeys(list_3))               #打印结果为{1: None, 2: None, 3: None}
print(dict3.fromkeys(list_3,"a"))           #打印结果为{1: 'a', 2: 'a', 3: 'a'}
	
#字典定义
dict1 = {"a":1,"b":2}					#{'a': 1, 'b': 2}
dict2 = dict(a=1,b=2)					#{'a': 1, 'b': 2}

#字典推导式
print({i: i * 2 for i in range(1, 3)})	#打印结果为{1: 2, 2: 4}

Menu:Python模块

项目目录结构

Python的程序结构
组成:
	包
	模块
		#包含python定义和语句的文件
		#.py文件
		#作为脚本运行
	方法

文件引用


模块定义
	
#模块导入
import 模块名
from <模块名> import <方法 | 变量 |>

#模块分类
1.系统内置模块 sys/os/time/正则re/json
2.第三方的开源模块,通过pip进行安装
pip install pyyaml
3.自定义模块


常用方法
dir()				#找出当前模块定义的对象,包含引用的变量、方法、对象
					#一般用于查看模块调用、使用的方法来源
					#创建模块命名时不要和系统模块命名冲突

dir(sys)			#找出参数模块sys定义的对象 
					
搜索路径
Python解析器对模块位置的搜索顺序:
	1.包含输入脚本的目录package(如果未指定文件,则为当前目录)
	2.PYTHONPATH(目录名称列表,语法与shell变量相同PATH)
	3.安装的默认路径
					

#方法调用
search()			#调用search的方法

#变量调用
search1				#调用search1的变量

Menu:Python输入与输出


字面量打印与格式化
字面量是以变量或常量给出的原始数据。在程序中可以直接使用字面量
字面量类型:
	数值型
	字符型
	布尔型
	字面量集合:列表list、元组tuple、字典dict和集合set
	特殊字面量:None
	
字面量插值,就是将变量、常量以及表达式插入的一种技术,它可以避免字符串拼接的问题,很多语言都支持了该功能
字面量插值方法:
	格式化输出
	通过string.format()方法拼接
	Formatted string literals,字符串格式化机制(>=python3.6)
	
字面量插值,格式化输出
	%的用法
		%s																#使用str()函数将表达式转换为字符串
		%d
		%f
		%.2f															#保留小数点后俩位
		%o																#八进制
name = 'test';age = 3
print("my name is %s,my age is %d,num is %f"%(name,age,3.1415)) 	    #打印结果为my name is test,my age is 6,num is 3.141500
	
	format()方法
	1.支持字符串   举例 print("we are the {} and {}" .format('tom','jerry'))
name = "lili";age = 18
print("my name is {},age is {}".format(name,age))										#打印结果为my name is lili,age is 6
print("my name is {0},age is {1}".format(name,age))										#{0}表示format()第一个值,{1}表示format()第二个值 打印结果为my name is lili,age is 6
	2.支持列表	   举例 print("we are the {0} and {1}" .format(*listdata))				#使用*对列表解包
	3.支持字典	   举例 print("my name is {name},my age is {age}" .format(**dictdata))	#使用**对字典解包  {name}:name是字典key {age}:age是字典key 
	
	Formatted string literals,字符串格式化机制											#必须满足Python>=python3.6,{}里面不能使用转义\
	f'{变量名}'

name2 = 'lili';age2 = 20;list2=[1,2,3];dict2={"name":"tom","gender":"male"}
print(f"my name is {name2},my age is {age2},my list is {list2[0]},my dict is {dict2['name']}") #这种取值不需要进行解包,取值方式为:列表{list2[0]} ,字典{dict2["name"]}
	
																						   #打印结果为my name is lili,my age is 20,my list is 1,my dict is tom
																						   
	name5 = "lili"
	#使用函数
	print(f"result is {name5.upper()}")             									#打印结果为result is LILI

	#使用表达式
	print(f"result is {(lambda x:x+1)(2)}")         									#使用表达式包含了: 必须使用()包裹起来  (2)代表的是x的传参为2
																						#打印结果为result is 3

文件读取
读写文件的操作步骤
	第一步:打开文件,获取文件描述符
	第二步:操作文件(读|写)
	第三部:关闭文件
	注意:文件读写操作完成后,应该及时关闭
	
	open(file,mode='r',buffering=-1,encoding=None)
		file:文件路径
		mode:只读r,写入w,追加a.默认文件访问为只读r
		#注意如果你要读取的文件内容为 图片,mode使用rb,使用二进制进行读取
		#正常文本使用rt ,也就是它的默认模式
		buffering:寄存区缓存
		encoding:编码格式

#with语句块,可以将文件打开之后,操作完毕,自动关闭该文件
with open('data.txt') as f:
	print(f.readlines())		#readlines() 读取所有行 ,readline()读取一行
	
#文件读取操作
with open("data.txt",'r') as f:
    while True:
        line = f.readline()                     #一行一行读
        if line:
            print(line)
        else:
            break
	
json:由字典、列表组成
json格式转换
	json.dumps(data1)		#将json转换为字符串
	json.loads(data2)		#将文件转换为json


#json读取
#定义json文件
json1 = {
    "name":['tom','nickname'],
    "age":1,
    "sex":1
}

print(type(json1))              #打印结果  <class 'dict'>
data1 = json.dumps(json1)
print(type(data1))              #打印结果   <class 'str'>
data2 = json.loads(data1)
print(type(data2))              #打印结果  <class 'dict'>   

Menu:Python错误与异常

语法错误与定位
异常捕获、异常处理
自定义异常

错误
	语法错误
	逻辑错误
	系统错误
	
异常
	程序执行过程中出现的未知错误
	语法和逻辑都是正常的
	程序业务逻辑不完善引起的程序漏洞

比如做除法,被除数未考虑为0的情况

错误与异常的区别?
	异常可以被捕获和处理
	错误一般是编码错误,逻辑错误,系统错误
	
常见的异常:
	除零类型、名称异常、索引异常、键异常、值异常、属性异常等等
	
程序里面,所有的异常都继承于BaseException

def div(a,b):
    return a/b

f = open("data.txt")

try:
    print(div(1, 0))                        #出现异常后,后面的语句不会被执行,直接跳到except语句
    list1=[1,2,3]
    print(list1[3])
    f.readlines()
except Exception as e:                      #使用Exception可以捕捉到所有的异常。
    print(e)                                #程序里面,所有的异常都继承于BaseException
    print("这里有个异常")
finally:                                    #不管有没有异常都会执行的代码
    print("finally")
    f.close()

打印结果为:
division by zero
这里有个异常
finally

异常捕获与异常处理
方案一:
try:
	执行代码
except:
	发生异常时执行的代码

方案二:
try:
	执行代码
except:
	发生异常时执行的代码
else:
	没有异常执行的代码

方案三:
try:
	执行代码
except:
	发生异常时执行的代码
else:
	没有异常执行的代码
finally:
	不管有没有异常都会执行的代码
	
使用raise手动抛出异常
def set_age(num):
	if num <=0 or num >200:
		raise ValueError					#主动抛出异常
		raise ValueError(f"值错误:{num}")	#主动抛出异常,也可以传值
	else:
		print(f"设置的年龄为:{num}")
		

#调用
set_age(-1)

自定义异常
class MyException(Exception): 				#继承Exception
	def __init__(self,msg):
		print(f"这是一个异常:{msg}")

Menu:Python面向对象编程

什么是面向对象?
	语言层面,封装代码和数据
	规格层面,对象是一系列可被使用的公共接口
	概念层面,对象是某种拥有责任的抽象
类、方法、类变量的定义
类:抽象的概念,一类事物
方法:类中定义的函数,对外提供的服务
类变量:类变量在整个实例化对象中是公用的

实例引用、实例变量使用
实例引用:实例化一个对象
实例变量:以'self.变量名'的方式定义的变量

类变量和实例变量的区别?
类变量需要类来访问,实例变量需要实例来访问。类变量和实例变量都可以被修改

类方法和实例方法的区别
类方法是不能访问实例方法,如果类方法想要访问实例方法,需要添加一个装饰器@classmethod,使其变为类方法去访问
#创建一个人类
#通过 class 关键字,定义了一个类

class Person:

    #属性
    #类变量
    name = "defalut"
    age = 0
    sex = "男"
    weight = 150

    #构造方法,在类实例化的时候被调用
    def __init__(self,name,age,sex,weight):
        #self.变量名的方式 访问的变量叫做实例变量
        self.name = name
        self.age = age
        self.sex = sex
        self.weight = weight


    #方法
    def eat(self):
        print(f"{self.name} eating")

    @classmethod            #类方法
    def jump(self):
        print(f"{self.name} jumping")

#类的实例化,创建了一个实例
zhangsan = Person("zhangsan",20,"女",90)
print(f"zhangsan的名字{zhangsan.name},zhangsan的年龄是{zhangsan.age}")
print(f"zhangsan的性别{zhangsan.sex},zhangsan的体重是{zhangsan.weight}")

print(Person.name)          #类变量需要类来访问
Person.name = "Tom"         #类变量和实例变量都可以被修改
print(Person.name)

print(zhangsan.name)        #实例变量需要实例来访问
zhangsan.name = "lili"      #类变量和实例变量都可以被修改
print(zhangsan.name)

print(zhangsan.eat())

#print(Person.eat())       #实例方法没有加装饰器类去访问会报错
                            #类不能访问实例方法,如果想要访问需要将方法添加一个装饰器@classmethod

print(Person.jump())        #类方法jump()加了装饰器

#打印的结果为:
zhangsan的名字zhangsan,zhangsan的年龄是20
zhangsan的性别女,zhangsan的体重是90
defalut
Tom
zhangsan
lili
lili eating
None
Tom jumping
None

Menu:Python标准库

Python标准库常见模块:
	操作系统相关:os
	时间与日期:time 、 datatime   #time、datatime是俩个模块哦
	科学计算:math
	网络请求:urllib


os模块主要是对文件、目录的操作
常用方法:
	os.mkdir()						#创建目录
	os.listdir("./")				#查看当前目录下所有文件和目录
	os.removedirs()					#删除文件
	os.getcwd()						#获取当前目录绝对路径
	os.path.exists(dir or file)		#判断文件或目录是否存在

#创建一个文件夹目录B,在目录B下面创建一个文件名为1.txt,内容为"hello,os"

print(os.path.exists("./b"))                #判断目录是否存在
if not os.path.exists("./b"):
    os.mkdir("./b")
if not os.path.exists("./b/1.txt"):         #判断文件是否存在
    f = open("./b/1.txt","w")
    f.write("hello,os")
    f.close()
	
time模块
获取当前时间以及时间格式的模块
	time.asctime()					#获取国外的时间格式
	time.time()						#获取当前的时间戳
	time.sleep()
	time.localtime()				#时间戳转换成时间元组
	time.strftime()					#将当前时间戳转成带格式的时间 ,时间戳转换成常用时间格式
	
print(time.time())
print(time.localtime())                         #时间戳转换成时间元组
print(time.strftime("%Y-%m-%d %H:%M:%S"))       #将当前时间戳转成带格式的时间 打印结果为2021-03-25 00:35:58
print(time.strftime("%Y:%m:%d %H:%M:%S"))       #strftime()注意里面参数传参,第二个参数为元组 打印结果为2021:03:25 00:38:48

#查询俩天前的当前时间
current_time = time.time()											#获取当前时间时间戳
two_day_before = current_time - 60 * 60 *24 *2
time_tuple = time.localtime(two_day_before)             #通过localtime()方法将时间转变为元组
print(time.strftime("%Y-%m-%d %H:%M:%S", time_tuple))   #将俩天前时间戳转成带格式的时间


urllib库
import urllib.request
response = urllib.request.urlopen('https://www.baidu.com')
print(response.status)
print(response.read())      #获取响应返回

math库
	math.ceil(x)	#返回大于等于参数x的最小整数
	math.floor(x)	#返回小于等于参数x的最大整数
	math.sqrt(x)	#平方根
	

print(math.ceil(5.4))       #打印结果为6
print(math.floor(5.1))      #打印结果为5
print(math.sqrt(16))        #打印结果为4.0

Menu:Python多线程处理

--------------多线程没看懂--------------
进程:
执行中的程序,拥有独立地址空间、内存、数据栈等
操作系统管理
派生(fork或spawn)新进程
进程间通信方式(IPC)共享信息

线程:	
同进程下执行,并共享相同的上下文
线程间的信息共享和通信更加容易
多线程并发 执行
需要同步原语	 (锁、信号量)
	
Python与线程
		解释器主循环
		主循环中只有一个控制线程在执行
		使用全局解释器锁(GIL)
	
GIL保证一个线程
	设置GIL
	切换进一个线程去运行
	执行下面操作之一	
		指定数量字节码指令
		线程主动让出控制权
	把线程设置回睡眠状态(切换出线程)
	解锁GIL
	重复上述步骤
	
	
俩种线程管理
	_thread:提供了基本的线程和锁
	threading:提供了更高级别、功能更全面的线程管理
		支持同步机制
		支持守护线程

_thread模块


threading模块

--------------多线程没看懂--------------

Menu:Python第三方库

pypi.org 搜索相关三方库,可以查看安装方法和一些使用方法
	Pytest
	安装 pip install pytest
	requests
	安装 pip install requests

#简单使用	
import requests

r_get = requests.get("http://www.baidu.com")
print(r_get.text)           #Content of the response, in unicode
print(r_get.content)        #返回二进制格式内容

Menu:pip依赖管理与虚拟环境

pypi托管了大量非常流行的库(www.pypi.org)
	
	pip install				#安装
	pip install -U 包名		#升级包
	pip uninstall  			#卸载
	pip list 				#列出所有的包文件

虚拟环境
不影响之前大环境,创建一个独立的环境完成项目

#命令行创建虚拟环境
创建虚拟环境
python -m venv tutorial-env  	#创建一个名称为	tutorial-env 的虚拟环境


进入虚拟环境:
cd tutorial-env

激活虚拟环境:
Windows: tutorial-env\Scripts\activate
Mac: source tutorial-env/bin/activate

查看虚拟环境包文件:在虚拟环境下pip list		#在tutorial-env下查看

deactivate										#退出虚拟环境

查看本机环境包文件:在本机环境下pip list		#在本机环境下查看

#Pycharm管理虚拟环境
新建项目的时候,Pycharm选择'New environment using...'表示创建的是虚拟环境,
勾选Inherit global site-packages表达的是将本机环境的所有包也引用于该虚拟环境
勾选'Make available to all projects'表达的是让虚拟环境应用于所有的项目
一般创建虚拟环境时不勾选该俩个选项,创建的虚拟环境应用于该项目文件夹下的所有文件

Menu:python unittest测试框架

单元测试概述
单元测试框架:Unittest、Pytest
单元测试覆盖率:
	代码覆盖率也被用于自动化测试和手工测试来度量测试是否全面的指标之一,应用覆盖率的思想增强测试用例的设计
	单元测试覆盖类型:
		语句覆盖
		条件覆盖
		判断覆盖
		路径覆盖
		
		
unittest框架介绍  https://docs.python.org/3/library/unittest.html 官网介绍 

unittest实战
Python自带的测试框架
Unittest提供了test cases、test suites、test fixtures
(在测试执行前和执行后进行必要的准备和清理工作,可以定义在模块、类、用例执行前后的工作,也就是setup/tearDown,或者测试场景,有需要登录,有不需要登录的场景,此时就可以使用fixture)、test runner相关的组件
编写规范:
	测试模块首先import unittest
	测试类必须继承unittest.TestCase,建议测试类名也以Test开头
	测试方法必须以"test_"开头

测试框架结构:
1.setup用来为测试准备环境,tearDown用来清理环境
2.如果想要在所有case执行之前准备一次环境,并在所有case
执行结束之后再清理环境,我们可以用setupClass()与tearDownClass()
比如:数据库连接及销毁 ,登录登出
3.如果想有些方法不在本次执行使用@unittest.skip
4.测试方法的命名:以test开头
5.各种执行-单一用例,全部

setUp、tearDown 方法是在每条测试用例前后分别调用的方法
setUpClass、tearDownClass方法 是在整个类前后分别调用的方法,setUpClass、tearDownClass属于类级别方法,需要添加装饰器
#这是一条例子
import unittest

class TestDemo(unittest.TestCase):

    #setUp、tearDown 方法是在每条测试用例前后分别调用的方法
    def setUp(self) -> None:    #代表默认返回值为None
        print("setup")

    def tearDown(self) -> None:
        print("teardown")

    #setUpClass、tearDownClass方法 是在整个类前后分别调用的方法
    @classmethod                #setUpClass属于类级别方法,需要添加装饰器
    def setUpClass(cls) -> None:
        print("setupclass")

    @classmethod                #tearDownClass属于类级别方法,需要添加装饰器
    def tearDownClass(cls) -> None:
        print("teardownclass")

    def test_upper(self):
        print("test_upper 1")
        self.assertEqual('foo'.upper(), 'FOO')

    def test_isupper(self):
        print("test_isupper 2")
        self.assertTrue('FOO'.isupper())
        self.assertFalse('Foo'.isupper())

    def test_split(self):
        print("test_split 3")
        s = 'hello world'
        self.assertEqual(s.split(), ['hello', 'world'])
        # check that s.split fails when the separator is not a string
        with self.assertRaises(TypeError):
            s.split(2)

    if __name__ == '__main__':          #调用unittest所有的测试类和测试用例
        unittest.main()

#打印结果为:
setupclass
setup
test_isupper 2
teardown


Ran 3 tests in 0.007s

OK
setup
test_split 3
teardown
setup
test_upper 1
teardown
teardownclass
unittest断言
assertEqual()			#断言相等
assertNotEqual()		#断言不相等
assertTrue()			#断言是否为True
assertFlase()			#断言是否为Flase

def test_equal(self):
	print("断言相等")
	self.assertEqual(1,1,"断言相等")
	
unittest执行测试用例
编写unittest测试用例的原则
	unittest会自动识别以test开头的函数是测试代码,test一定要是小写
执行测试用例的顺序
	测试用例执行顺序是以test后面的字母顺序执行的。例如test_a,test_b,test_c

执行方法:
	执行方法一:
	unittest.main()			#调用unittest所有的测试类和测试用例
	
	执行方法二: 加入容器中执行,测试套件		#执行指定的测试用例,将要执行的测试用例添加到测试套件里面,批量执行
	suite = unittest.TestSuite()				#创建一个测试套件
	suite.addTest(TestMethod("test_01"))
	suite.addTest(TestMethod("test_02"))
	unittest.TextTestRunner().run(suite)
	
	###如果通过IDE来执行,执行结果和期望对不上,可通过命令行的方式来执行,查看测试结果
	
	#执行方法三:执行某个测试类		#此方法可以同时测试多个类,将要执行的测试类添加到测试套件里面,批量执行
	suite1 = unittest.TestLoader().loadTestsFromTestCase(TestCase1)			#加载测试类
	suite2 = unittest.TestLoader().loadTestsFromTestCase(TestCase2)
	suite = unittest.TestSuite([suite1,suite2])
	unittest.TextTestRunner(verbosity=2).run(suite)
	
	#执行方法四:匹配某个目录下所有以test开头的py文件,执行这些文件下所有的测试用例
	test_dir = './test_case'
	discover = unittest.defaultTestLoader.discover(test_dir,pattern="test*.py")
		#discover可以一次调用多个脚本
		#test_dir被测试脚本的路径
		#pattern脚本名称匹配规则
	unittest.TextTestRunner(verbosity=2).run(discover)

unittest结合htmltestrunner生成带日志的测试报告
https://www.github.com/huilansame/HTMLTestRunner_PY3  	#PY3
#htmltestrunner是第三方生成测试报告的工具,了解即可,后面报告会使用allure

Menu:pytest测试框架

测试用例的识别与运行
	测试文件:
		1.test_*.py
		2.*_test.py
	
	用例识别
		1.Test*类包含的所有test_*的方法(测试类不能带有__init__方法)
		2.不在class中的所有test_*方法
	
	pytest也可以执行unittest框架写的用例和方法
	
运行:
pytest			#使用pytest解释器,执行当前目录下所有的识别文件
				#使用Python解释器也是可以运行pytest的测试用例的,通过入口函数main	

#使用Python解释器运行pytest的测试用例,通过入口函数main	

#举例	
import pytest

def func(x):
    return x + 1

def test_answer():
    assert func(2) == 3

#使用Python解释器也是可以运行pytest的测试用例的
if __name__ == '__main__':                  
    pytest.main(['test_a.py'])              #test_a.py为运行文件路径
pytest 	#第三方库 安装 pip install pytest
	
	
pytest
		-v 		#查看更详细的信息
		-k		#给定指定的测试用例名字去执行指定的测试用例

参数化:
@pytest.mark.parametrize()		#参数以外部文件或外部数据传入进来
import pytest

def func(x):
    return x + 1
	
@pytest.mark.parametrize("a,b",[              #注意参数自定义的写法 "a,b" ,多组参数以列表的方式进行传递
    (1,2),
    ("a","b"),
    ("a","a1"),
    (4,5),
    (5,6)
])
def test_answer(a,b):
    assert func(a) == b
fixture功能
比如测试场景,有需要登录,有不需要登录的场景,此时就可以使用fixture
#举例
import pytest

@pytest.fixture()                   #添加装饰器
def login():
    username = 'Jerry'
    return username

class TestDemo:
    '''
    test_a需要登录
    test_b不需要登录
    test_c需要登录
    '''								#特别注意
    def test_a(self,login):         #将加了装饰器的函数传入,执行该测试用例时会优先调用login()
									###注意传入的不是变量,而是函数
        print(f"a username = {login}")

    def test_b(self):
        print("b")

    def test_c(self,login):
        print(f"c username = {login}")

打印结果为:

test_a.py::TestDemo::test_a PASSED                                       [ 33%]a username = Jerry

test_a.py::TestDemo::test_b PASSED                                       [ 66%]b

test_a.py::TestDemo::test_c PASSED                                       [100%]c username = Jerry

Menu:参数化用例

参数化使用
方法一:
@pytest.mark.parametrize(argnames,argvalues)
	#argnames:要参数化的变量,string(逗号分隔),list,tuple
	#argvalues:参数化的值,list,list[tuple]

	#使用string
	@pytest.mark.parametrize('a,b',[
        (10,20),
        (20,30),
        (40,50)
    ])


    def test_param(self,a,b):
        print(a+b)
		
	打印结果为:
	PASSED                            [ 33%]30
	PASSED                            [ 66%]50
	PASSED                            [100%]90


	#使用list,数据可以修改,通过列表属性可以查看
    @pytest.mark.parametrize(['c','d'],[
        (10, 20),
        (20,30),
        (40,50)
    ])

    def test_param(self,c,d):
        print(c+d)
	
	打印结果为:
	PASSED                            [ 33%]30
	PASSED                            [ 66%]50
	PASSED                            [100%]90
		
	#使用tuple,数据不可修改
    @pytest.mark.parametrize(('c', 'd'), [
        (10, 20),
        (20, 30),
        (40, 50)
    ])

    def test_param(self,c,d):
        print(c+d)
	
	打印结果为:
	PASSED                            [ 33%]30
	PASSED                            [ 66%]50
	PASSED                            [100%]90

方法二:yaml数据参数化					#需要加强yaml文档的编写,下面yaml数据可作为参考

pip install pyYaml			#安装yaml 	

#list
  - 10						#注意空格
  - 20
  - 30
  
#结果为:
[
  10,
  20,
  30
]

#list
-
  - 10
  - 20
  - 30
  
#结果为:
[
  [
    10,
    20,
    30
  ]
]


#dict
  dict: 6					#注意空格
  by: id
  locator: name
  action: click
  
结果为:
{
  "dict": 6,
  "by": "id",
  "locator": "name",
  "action": "click"
}

#dict
companies:
  dict: 6
  by: id
  locator: name
  action: click
  
结果为:
{
  "companies": {
    "dict": 6,
    "by": "id",
    "locator": "name",
    "action": "click"
  }
}
	
yaml嵌套,实现二维数组
-								#list嵌套字典
  dict: 6
  by: id
  locator: name
  action: click
	
结果为:
[
  {
    "dict": 6,
    "by": "id",
    "locator": "name",
    "action": "click"
  }
]
	
companies:
-  
  id: 1
  name: company1
  price: 200W
-
  id: 2
  name: company2
  price: 500W
		
结果为:
{
  "companies": [
    {
      "id": 1,
      "name": "company1",
      "price": "200W"
    },
    {
      "id": 2,
      "name": "company2",
      "price": "500W"
    }
  ]
}

加载yaml文件:
yaml.safe_load(open("./data.yml"))		#yaml文件后缀名:yml


#yaml文件内容:
-
  - 10
  - 20
-
  - 30
  - 19


#[[10,20], [30,19]] 	

class TestDemo:

    @pytest.mark.parametrize(["a", "b"], yaml.safe_load(open("./data.yml")))
    def test_para(self, a, b):
        print(a+b)

#打印结果为:
PASSED                          [ 50%]30
PASSED                          [100%]49

Menu:数据驱动

数据驱动简介:简单来说,就是参数化的应用。数据量大的情况建议大家使用一种结构化的文件(例如yaml、
json等)来对数据进行存储,然后在测试用例中读取这些数据


数据驱动应用场景:
App、Web、接口自动化测试
	测试步骤的数据驱动		#点击、sendkeys等
	测试数据的数据驱动		#大量数据参数化
	配置的数据驱动			#切换环境(测试环境,开发环境,host地址等)
#数据驱动案例
import pytest
import yaml

'''
#yml文件内容
-
  test:
    - env: 127.0.0.1
  dev:
    - env: 192.168.0.1
'''

class TestDemo:

    @pytest.mark.parametrize('env',yaml.safe_load(open("./env.yml")))
    def test_demo(self,env):
        if "test" in env:
            print("这是测试环境")
            '''
            test: 127.0.0.1
            '''
            print(env)                              #当yaml文件为字典文件时,这里只能取到key的值   字典为{"test":"127.0.0.1"}
            '''
            -
              test: 127.0.0.1
            '''
            print("测试环境ip地址为:",env["test"])    #当yaml文件为list时,则可以取到所有的key值和value值   数组为[{"test":"127.0.0.1"}]
        elif "dev" in env:
            print("这是开发环境")
            print("开发环境ip地址为:",env["dev"])

Menu:测试报告美化与定制

allure介绍
支持多语言、多平台,可以集成到Jenkins

安装:
一、allure安装
windows/mac通用安装方法:
	1.https://repo1.maven.org/maven2/io/qameta/allure/allure-commandline/
	2.下载allure2.7.zip包
	3.解压->进入bin目录->运行allure.bat
	4.把bin目录加入到PATH环境变量
Mac可以使用brew安装
	brew install allure
	#Mac和Linux不需要配置环境变量,安装会直接安装在系统目录下,不需要配置环境变量就可直接运行
	
安装完成后通过命令行输入allure查看allure是否安装完成
windows查询pytest帮助文档过滤使用findstr ,Linux和Mac使用grep
Linux: pytest --help | grep allure

Windows: pytest --help | findstr allure  	#findstr命令用于windows系统,该命令在pycharm,pytest运行环境下运行


allure官网:http://allure.qatools.ru	

二、安装allure-pytest插件
pip install allure-pytest 	#在python环境下安装allure-pytest插件

运行:
	在测试执行期间收集结果:
		pytest [测试文件] -s -q --alluredir=./result/				
		#--alluredir用于指定存储测试结果的路径; 
		#-s 等价于 pytest --capture=no 可以捕获print函数的输出
		#-q 该参数选项与 -v 整好相反,是简化输出;
				
	查看测试报告:
		方式一:测试完成后查看实际报告,在线看报告,会直接打开默认浏览器展示当前报告
			allure serve ./result/ 									#注意这里的serve书写
		
		方式二:从结果生成报告,这是一个启动tomcat的服务,需要俩个步骤:生成报告,打开报告
			生成报告
				allure generate ./result/ -o ./report/ --clean		#覆盖路径加上--clean  -o指定生成报告的存放路径
			查看报告 
			#如果想不发送任何文件,也可以让其他人访问本地的allure报告,可以通过本地开端口
				allure open -h 127.0.0.1 -p 8883 ./report/			#report为测试报告结果的路径文件生成文件夹路径

Allure常用特性分析
场景:
	希望在报告中看到测试功能,子功能或场景,测试步骤,包括测试附加信息
解决:
	@Feature , @story , @step , @attach
步骤:
	import allure
	功能上加@allure.feature('功能名称')				#feature和story类似于父子关系
	子功能上加@allure.story('子功能名称')
	步骤上加@allure.step('步骤细节')
	@allure.attach('具体文本信息'),需要附加的信息,可能是数据、文本、图片、视频、网页
	如果只测试登录功能运行的时候可以加限制过滤:
		pytest 文件名 --allure-features '购物车功能' --allure-stories '加入购物车'

allure特性step
用法:
	@allure.step('步骤细节')			#只能以装饰器的形式放在类或者方法上面
	with allure.step()					#可以放在测试用例方法里面,但测试步骤的代码需要被该语句所包含

pytest test_feature_story.py  --allure-features="搜索模块"  -v 				#查询文件test_feature_story.py名称为"搜索模块"的所有features  -v参数查看详情
		
pytest test_feature_story.py  --allure-stories="搜索失败"  -v				#查询文件test_feature_story.py名称为"搜索失败"的所有stories	 -v参数查看详情

pytest test_feature_story.py  --allure-features="搜索模块"  -v --alluredir=./result/				#在测试执行期间收集结果,--alluredir用于指定存储测试结果的路径

allure serve ./result/ 			#在线看报告

#举个栗子
import pytest
import allure

@allure.feature("搜索模块")
class TestSearch():

    @allure.story("搜索成功")
    def test_search(self):
        print("搜索成功")

    @allure.story("搜索失败")
    def test_search(self):
        print("搜索失败")

@allure.feature("登录模块")
class TestLogin():
    @allure.story("登录成功")
    def test_login_success(self):

        with allure.step("步骤1:打开应用"):
            print("打开应用")

        with allure.step("步骤2:进入登录页面"):
            print("进入登录页面")

        with allure.step("步骤3:输入用户名和密码"):
            print("输入用户名和密码")

        print("这是登录:测试用例,登录成功")
        pass

    @allure.story("登录失败")
    def test_login_fail(self):
        print("这是登录:测试用例,登录失败")
        pass

    @allure.story("用户名缺失")
    def test_login_success_b(self):
        print("用户名缺失")
allure特性testcase
关联测试用例(可以直接给测试用例的地址链接)
	#例子
	TEST_CASE_LINK = 'https://github.com/qameta/allure/allure-commandline/'			#链接为测试case的链接
	@allure.testcase(TEST_CASE_LINK,'Test Case Title')
	def test_with_testcase_link():
		pass
		
	pytest [测试文件]  --alluredir=./result2/							    #--alluredir用于指定存储测试结果的路径
	
	#方法一:查看报告
	allure serve ./result2/ 												#注意这里的serve书写,在线看报告
	
	#方法二:查看报告
	allure generate ./result2/							#如果不指定生成文件夹路径,默认生成一个allure-report的文件夹,文件夹下找到index.html文件,鼠标右键可以通过浏览器进行打开
		
	allure generate ./result2/	-o report2				#-o默认指定生成文件夹 ,找到文件夹下的index.html文件,鼠标右键可以通过浏览器进行打开
	
	allure generate ./result/ -o ./report2/ --clean		#覆盖路径加上--clean
	
	#通过上述步骤则可以在allure报告总览查看链接地址啦

allure特性title
将方法名称以自定义的名称在allure报告里面进行展示
#举个栗子		
import allure
import pytest

TEST_CASE_LINK = "https://github.com/qameta/allure/allure-commandline/"

@allure.testcase(TEST_CASE_LINK,"测试链接地址")
@allure.title("测试用例链接地址")					#将方法名称以自定义的名称在allure报告里面进行展示
def test_with_testcase_link():
    pass
按重要性级别进行一定范围测试
场景:
	通常测试有P0、冒烟测试、验证上线测试。按重要性级别来分别执行

解决:	
	1.通过附加pytest.mark标记
	2.通过allure.feature,allure.story
	3.也可以通过allure.severity来附加标记
		级别:Trivial:不重要 Minor:不太重要 Normal:一般 Critical:严重 Blocker:阻塞

allure.severity来附加标记 步骤:
	在方法,函数和类上面加
		@allure.severity(allure.serverity_level.TRIVIAL)
	执行时
		pytest -s -v 文件名 -allure-severities="normal"					#运行级别为normal的用例,记住windows的朋友请用双引号"normal",不要使用单引号'normal'


Allure报告中嵌入文本、图片、视频等资源
场景:
	前端自动化测试经常需要附加图片或html,在适当的地方,适当的时机截图
解决:	
	@allure.attach显示不同类型的提供的附件,可以补充测试,步骤或测试结果
步骤:
	在测试报告里附加网页:
		allure.attach(body(内容),name,attachment_type,extension):
		#举例:	allure.attach('<head></head><body>首页</body>,'这是错误页的结果信息',allure.attachment_type.HTML')
	在测试报告里附加图片:
		allure.attach.file(source,name,attachment_type,extension):
		#举例:	allure.attach.file("./result/b.png",attachment_type=allure.attachment_type.PNG)
	在测试报告里附加文本:
		allure.attach("这是一个文本",attachment_type=allure.attachment_type.TEXT)
	在测试报告里附加视频:
		#文件类型和给定的图片文件类型一致即可
		#Windows拷贝路径地址时,记得使用r参数格式化地址
		#r的作用是去除转义字符
		#r'input\n' # 非转义原生字符,经处理’\n’变成了’\‘和’n’。
			#也就是\n表示的是两个字符,而不是换行。 结果为 'input\n'
		allure.attach.file(r"C:\Users\Banana\Desktop\短视频_百度搜索.mp4","这是一段视频",attachment_type=allure.attachment_type.MP4)
#举个栗子		
import allure

def test_attach_text():
    #如果不指定文件名会默认随机生成文件名哦
    allure.attach("这是一个文本",attachment_type=allure.attachment_type.TEXT)

def test_attach_html():
    #如果html内容有单双引号,可以通过三个单引号来包裹
    allure.attach('''<head></head><body>首页</body>''',"这是错误页的结果信息",allure.attachment_type.HTML)

def test_attach_photo():
    #文件类型和给定的图片文件类型一致即可
    allure.attach.file("/Users/Banana/Desktop/dog.jpg","这是一张图片",attachment_type=allure.attachment_type.JPG)

def test_attach_video():
    #文件类型和给定的图片文件类型一致即可
    #Windows拷贝路径地址时,记得使用r参数格式化地址
    allure.attach.file(r"C:\Users\Banana\Desktop\短视频_百度搜索.mp4","这是一段视频",attachment_type=allure.attachment_type.MP4)


#pytest test_attach.py --alluredir=./result2/
#allure serve result2

#如果想不发送任何文件,也可以让其他人访问本地的allure报告,可以通过本地开端口
	#allure open -h 127.0.0.1 -p 8883 ./report/				#report为测试报告结果的路径文件生成文件夹路径
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值