python模块学习笔记
- 1.Python自动发送邮件smtplib
- 2.制作二维码图片MyQR
- 3.绝对值模块math
- 4.CSV模块
- 5.openpyxl 模块,操作Excel文件 ExcelManual
- 6.os模块
- 7.requests模块
- 8.BeautifulSoup 模块 解析数据+提取数据
- 9.Request Headers的用法
- 10.gevent模块,协程和异步
- 11.configparser 模块读写配置文件
- 12.logging 日志模块 #Python自带模块
- 13.unittest 单元测试模块
- 14.ddt模块,装饰器,只能和unittest搭配使用
- 15.mock 数据生成,造假数据
- 16.pytest测试框架,安装pip install pytest
1.Python自动发送邮件smtplib
import smtplib
from email.mime.text import MIMEText
from email.header import Header
qq_host = "smtp.qq.com" # 发信服务器
qq_port = "465" # 发信服务器端口
qq_username = 'XXXXXXX@qq.com' # 发信用户名
qq_password = "XXXXXXXXX" # 发信授权码,代替登录密码
from_addr = qq_username
to_addr = ['XXXXXX@qq.com']
msg = MIMEText('自动发送邮件测试', 'plain', 'utf-8')
msg['From'] = Header(",".join(to_addr))
msg['To'] = Header("chen")
msg['Subject'] = Header('python邮件')
server = smtplib.SMTP_SSL(qq_host)
server.connect(qq_host, qq_port) # 输入服务器地址和端口号
server.login(qq_username, qq_password) # 输入邮箱用户名 和授权码
server.sendmail(from_addr, to_addr, msg.as_string())
server.quit()
#--------------------------------------------------------------------------------------------------#
2.制作二维码图片MyQR
from MyQR import myqr
myqr.run(
words='http://weixin.qq.com/r/kzlje9TEE4lsrZAY92yB',
# 扫描二维码后,显示的内容,或是跳转的链接
version=5, # 设置容错率
level='H', # 控制纠错水平,范围是L、M、Q、H,从左到右依次升高
picture='tu.gif', # 图片所在目录,可以是动图
colorized=True, # 黑白(False)还是彩色(True)
contrast=1.0, # 用以调节图片的对比度,1.0 表示原始图片。默认为1.0。
brightness=1.0, # 用来调节图片的亮度,用法同上。
save_name='Python.gif', # 控制输出文件名,格式可以是 .jpg, .png ,.bmp ,.gif
)
#--------------------------------------------------------------------------------------------------#
3.绝对值模块math
import math
方法1:条件判断
def abs_value1():
a = float(input('1.请输入一个数字:'))
if a >= 0:
a = a
else:
a = -a
print('绝对值为:%f' % a)
方法2:内置函数 abs()
def abs_value2():
a = float(input('2.请输入一个数字:'))
a = abs(a)
print('绝对值为:%f' % a)
方法3:内置模块 math
def abs_value3():
a = float(input('3.请输入一个数字:'))
a = math.fabs(a)
print('绝对值为:%f' % a)
运行函数,查验一下。
abs_value1()
abs_value2()
abs_value3()
#--------------------------------------------------------------------------------------------------#
4.CSV模块
(1)写文件
import csv
with open("demo.csv","w",newline="",encoding="utf-8") as demo :
writer = csv.writer(demo)
writer.writerow(["chen","yong","qi"])
writer.writerow(['电影','豆瓣评分'])
(2)读文件
with open("demo.csv","r",newline="",encoding="utf-8") as demo:
reader = csv.reader(demo)
for i in reader:
print(i)
print(type(reader))
#--------------------------------------------------------------------------------------------------#
5.openpyxl 模块,操作Excel文件 ExcelManual
(1)引用openpyxl模块新建工作簿和工作表
import openpyxl
#引用openpyxl 。
wb = openpyxl.Workbook()
#创建新的workbook(工作薄)对象,就是创建新的空的Excel文件。
sheet = wb.active
#就是获取这个工作薄的活动表,通常就是第一个工作表。
sheet.title = ‘new title’
#可以用.title给工作表重命名。现在第一个工作表的名称就会由原来默认的“sheet1”改为"new title"。
(2)写入数据,操作工作表
sheet.cell(row=1, column=1).value = “第一种方法”
sheet[‘A1’] = ‘第二种方法’
sheet.append([‘美国队长’,‘钢铁侠’,‘蜘蛛侠’])#第三种方法在文件末尾添加
(3)写入多行
rows = [[‘美国队长’,‘钢铁侠’,‘蜘蛛侠’],[‘是’,‘漫威’,‘宇宙’, ‘经典’,‘人物’]]
#先把要写入的多行内容写成列表,再放进大列表里,赋值给rows。
for i in rows:
sheet.append(i)
#遍历rows,同时把遍历的内容添加到表格里,这样就实现了多行写入。
print(rows)
#打印rows
wb.save(‘Marvel.xlsx’)
#保存新建的Excel文件,并命名为“Marvel.xlsx” 注:“成功写入后,我们千万要记得保存这个Excel文件,不然就白写啦!”
(4)向已有的工作簿写入数据
wb = openpyxl.load_workbook(‘Marvel.xlsx’)#打开工作簿,参数为文件路径
sheet = wb[‘new title’]#选择表单
sheetname = wb.sheetnames
print(sheetname)
A1_cell = sheet[‘A1’]
A1_value = A1_cell.value
print(A1_value)
--------补充内容---------
(5)创建表(sheet)
#方式一:插入到最后(default)
ws1 = wb.create_sheet(“Mysheet”)
#方式二:插入到最开始的位置
ws2 = wb.create_sheet(“Mysheet”, 0)
(6)选择表(sheet)
#sheet 名称可以作为 key 进行索引
ws3 = wb[“New Title”]
ws4 = wb.get_sheet_by_name(“New Title”)
ws is ws3 is ws4
True
(7) 查看表名(sheet)
#显示所有表名#必须加title取出表名
print(wb.sheetnames)
[‘Sheet2’, ‘New Title’, ‘Sheet1’]
#遍历所有表
for sheet in wb:
print(sheet.title)
(8)获取表单所有方法
sheet = wb.worksheets#获取所有表单返回列表
sheet = wb.worksheets[2]#选择一个
sheet = wb[“sheet1”]#
(9)读取单元格的方法
A1_cell = sheet.cell(1,1)#输入坐标
A1_cell = sheet[‘A1’]
cell_range = ws[‘A1’:‘C2’]#切片获取
(10)获取每一行,每一列
sheet.rows #为生成器, 里面是每一行的数据,每一行又由一个tuple包裹。
sheet.columns #类似,不过里面是每个tuple是每一列的单元格。
(11)获取最大行和最大列
print(sheel.max_row)
print(sheel.max_column)
(12)根据数字得到字母,根据字母得到数字
from openpyxl.utils import get_column_letter, column_index_from_string
#根据列的数字返回字母
print(get_column_letter(2)) # B
#根据字母返回列的数字
print(column_index_from_string(‘D’)) # 4
(13)删除工作表
#方式一
wb.remove(sheet)
#方式二
del wb[sheet]
(19)单元格填充颜色
from openpyxl.styles import PatternFill
fill = PatternFill(start_color =‘FFFF00’, end_color = ‘FFFF00’, fill_type = ‘solid’) #设置单元格填充黄色
#fill = PatternFill() #设置单元格为无填充
ws1.cell(row = 1, column = 1, value = 10).fill = fill
RGB颜色查询对照表链接:http://www.114la.com/other/rgb.htm
#--------------------------------------------------------------------------------------------------#
6.os模块
import os
#os常用方法
os.mkdir("python22") #新建一个目录,只能创建一层目录
os.makedirs('dirname1/dirname2') #生成多层递归目录,可以创建多层不存在的目录
os.rmdir("python22") #删除一个空目录
os.remove(path) #删除文件,只能删除文件
os.removedirs(path) #删除空文件夹
os.path.exists("python22") #判断文件夹是否存在,判断文件是否存在,判断路径是否存在,若存在返回True,否则返回False
os.path.isdir("python22") #判断是否是目录是返回True
os.path.isfile("py22.txt") #判断是否是文件是返回True
path1 = os.getcwd() #获取当前文件的工作目录不包括文件名
path2 = os.path.basename(__file__) #返回文件名
path3 = os.path.realpath(__file__) #返回绝对路径+文件名
path4 = os.path.abspath(path) #返回绝对路径
path5 = os.path.split(path3) #切割当前路径,切掉最后一级,返回一个元组,可用a[0],a[1]获取
path6 = os.path.dirname(path) #返回上一级文件路径,去掉一级
os.path.join(path6,"a.txt") #拼接路径,把目录和文件名合成一个路径
#os补充方法
os.unlink() #方法用于删除文件,如果文件是一个目录则返回一个错误。
os.path.abspath(path) 返回绝对路径
os.path.basename(path) 返回文件名
os.path.commonprefix(list) 返回list(多个路径)中,所有path共有的最长的路径
os.path.dirname(path) 返回上一级文件路径,去掉一级
os.path.exists(path) 如果路径 path 存在,返回 True;如果路径 path 不存在,返回 False。
os.path.lexists 路径存在则返回True,路径损坏也返回True
os.path.expanduser(path) 把path中包含的"~"和"~user"转换成用户目录
os.path.expandvars(path) 根据环境变量的值替换path中包含的"$name"和"${name}"
os.path.getatime(path) 返回最近访问时间(浮点型秒数)
os.path.getmtime(path) 返回最近文件修改时间
os.path.getctime(path) 返回文件 path 创建时间
os.path.getsize(path) 返回文件大小,如果文件不存在就返回错误
os.path.isabs(path) 判断是否为绝对路径
os.path.isfile(path) 判断路径是否为文件
os.path.isdir(path) 判断路径是否为目录
os.path.islink(path) 判断路径是否为链接
os.path.ismount(path) 判断路径是否为挂载点
os.path.join(path1[, path2[, ...]]) 把目录和文件名合成一个路径
os.path.normcase(path) 转换path的大小写和斜杠
os.path.normpath(path) 规范path字符串形式
os.path.realpath(path) 返回path的真实路径
os.path.relpath(path[, start]) 从start开始计算相对路径
os.path.samefile(path1, path2) 判断目录或文件是否相同
os.path.sameopenfile(fp1, fp2) 判断fp1和fp2是否指向同一文件
os.path.samestat(stat1, stat2) 判断stat tuple stat1和stat2是否指向同一个文件
os.path.split(path) 把路径分割成 dirname 和 basename,返回一个元组
os.path.splitdrive(path) 一般用在 windows 下,返回驱动器名和路径组成的元组
os.path.splitext(path) 分割路径,返回路径名和文件扩展名的元组
os.path.splitunc(path) 把路径分割为加载点与文件
os.path.walk(path, visit, arg) 遍历path,进入每个目录都调用visit函数,visit函数必须有3个参数(arg, dirname, names),
dirname表示当前目录的目录名,names代表当前目录下的所有文件名,args则为walk的第三个参数
os.path.supports_unicode_filenames 设置是否支持unicode路径名
#--------------------------------------------------------------------------------------------------#
7.requests模块
#扩展:如何查看网站的robots协议:在网站的域名后加上/robots.txt就可以了。
import requests
URL = "https://localprod.pandateacher.com/python-manuscript/crawler-html/sanguo.md"
res = requests.get(URL)
#打开网页并存到res变量,或者实例化Response对象
print(res.status_code)
#打印变量res的响应状态码,以检查请求是否成功200成功
res = res.content
#把Reponse对象的内容以二进制数据的形式返回
wen = res.text
#把Response对象的内容以字符串的形式返回
res.encoding='utf-8'
#重定义Reponse对象的编码为utf-8。
#requests库会对数据的编码类型做出自己的判断。
#如果判断不准确,就会出现一堆乱码,那我们就可以去查看目标数据的编码,
#然后再用res.encoding把编码定义成和目标数据一致的类型即可。
with open("三国演义.txt","a")as san:
san.write(wen)
print(wen[:80])
#--------------扩展保存一首歌曲-------------------------------------------------------------#
import requests
url = "https://static.pandateacher.com/Over%20The%20Rainbow.mp3"
res = requests.get(url)
print(res.status_code)
musik = res.content
with open("英文歌曲.mp3","wb") as m:
m.write(musik)
print("歌曲已保存完毕")
print(len(musik))
#--------------------------------------------------------------------------------------------------#
8.BeautifulSoup 模块 解析数据+提取数据
(1)简单版代码
import requests
from bs4 import BeautifulSoup
url = 'https://localprod.pandateacher.com/python-manuscript/crawler-html/spder-men0.0.html'
res = requests.get (url)
print(res.status_code)
#打印变量res的响应状态码,以检查请求是否成功200成功
soup = BeautifulSoup(res.text,'html.parser')
#把网页解析为BeautifulSoup对象
item = soup.find('div')
#使用find()方法提取首个<div>元素,并放到变量item里。
items = soup.find_all('div')
#用find_all()把所有符合要求的数据提取出来,并放在变量items里
items = soup.find_all(class_='books')
# 通过匹配标签和属性提取我们想要的数据
print(type(items)) #打印items的数据类型
(2)详细版代码
#注:Tag.text提出Tag对象中的文字,用Tag['href']提取出URL。
import requests # 调用requests库
from bs4 import BeautifulSoup # 调用BeautifulSoup库
url = 'https://localprod.pandateacher.com/python-manuscript/crawler-html/spider-men5.0.html'
res =requests.get(url)
# 返回一个response对象,赋值给res
print(res.status_code)
#打印变量res的响应状态码,以检查请求是否成功200成功
res.encoding='utf-8'
#重定义Reponse对象的编码为utf-8。
html=res.text
# 把res解析为字符串
soup = BeautifulSoup( html,'html.parser')
# 把网页解析为BeautifulSoup对象
item = soup.find('div')
#使用find()方法提取首个<div>元素,并放到变量item里。
items = soup.find_all(class_='books')
# 通过匹配属性class='books'提取出我们想要的元素
for item in items: # 遍历列表items
kind = item.find('h2')
# 在列表中的每个元素里,匹配标签<h2>提取出数据
title = item.find(class_='title')
# 在列表中的每个元素里,匹配属性class_='title'提取出数据
brief = item.find(class_='info')
# 在列表中的每个元素里,匹配属性class_='info'提取出数据
print(kind.text,'\n',title.text,'\n',title['href'],'\n',brief.text)
# 打印书籍的类型、名字、链接和简介的文字
#------------------复制粘贴用---------------------------------------#
#text获取到的是该标签内的纯文本信息,即便是在它的子标签内,也能拿得到。
#但提取属性的值,只能提取该标签本身的。
import requests
from bs4 import BeautifulSoup
url = "https://localprod.pandateacher.com/python-manuscript/crawler-html/spider-men5.0.html"
res = requests.get(url)
print(res.status_code)
html = res.text
soup = BeautifulSoup( html,'html.parser')
items = soup.find_all(class_='books')
for item in items:
kind = item.find('h2')
title = item.find(class_='title')
brief = item.find(class_='info')
print(kind,'\n',title,'\n',brief)
print(type(kind),type(title),type(brief))
#------------------复制粘贴用---------------------------------------#
#--------------------------------------------------------------------------------------------------#
9.Request Headers的用法
import requests
url = 'https://c.y.qq.com/base/fcgi-bin/fcg_global_comment_h5.fcg'
# 这是那个,请求歌曲评论的url
headers = {
'origin':'https://y.qq.com',
# 请求来源,本案例中其实是不需要加这个参数的,只是为了演示
'referer':'https://y.qq.com/n/yqq/song/004Z8Ihr0JIu5s.html',
# 请求来源,携带的信息比“origin”更丰富,本案例中其实是不需要加这个参数的,只是为了演示
'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
71.0.3578.98 Safari/537.36',
# 标记了请求从什么设备,什么浏览器上发出
}
params = {
'g_tk':'5381',
'loginUin':'0',
'hostUin':'0',
'format':'json',
'inCharset':'utf8',
'outCharset':'GB2312',
'notice':'0',
'platform':'yqq.json',
'needNewCode':'0',
'cid':'205360772',
'reqtype':'2',
'biztype':'1',
'topid':'102065756',
'cmd':'8',
'needcommentcrit':'0',
'pagenum':0,
'pagesize':'25',
'lasthotcommentid':'',
'domain':'qq.com',
'ct':'24',
'cv':'101010 '
}
res_music = requests.get(url,headers=headers,params=params)
# 发起请求
#--------------------------------------------------------------------------------------------------#
10.gevent模块,协程和异步
from gevent import monkey
#从gevent库里导入monkey模块。
monkey.patch_all()
#monkey.patch_all()能把程序变成协作式运行,就是可以帮助程序实现异步。
import gevent,time,requests
#导入gevent、time、requests
from gevent.queue import Queue
#从gevent库里导入queue模块
start = time.time()
url_list = ['https://www.baidu.com/',
'https://www.sina.com.cn/',
'http://www.sohu.com/',
'https://www.qq.com/',
'https://www.163.com/',
'http://www.iqiyi.com/',
'https://www.tmall.com/',
'http://www.ifeng.com/']
work = Queue()
#创建队列对象,并赋值给work。
for url in url_list:
#遍历url_list
work.put_nowait(url)
#用put_nowait()函数可以把网址都放进队列里。
def crawler():
while not work.empty():
#当队列不是空的时候,就执行下面的程序。
url = work.get_nowait()
#用get_nowait()函数可以把队列里的网址都取出。
r = requests.get(url)
#用requests.get()函数抓取网址。
print(url,work.qsize(),r.status_code)
#打印网址、队列长度、抓取请求的状态码。
tasks_list = [ ]
#创建空的任务列表
for x in range(2):
#相当于创建了2个爬虫
task = gevent.spawn(crawler)
#用gevent.spawn()函数创建执行crawler()函数的任务。
tasks_list.append(task)
#往任务列表添加任务。
gevent.joinall(tasks_list)
#用gevent.joinall方法,执行任务列表里的所有任务,就是让爬虫开始爬取网站。
end = time.time()
print(end-start)
#--------------------------------------------------------------------------------------------------#
11.configparser 模块读写配置文件
#什么是配置文件 .conf .ini .config .properties .xml
#怎么写配置文件 ---[section] #片段名
#option=value也可以用option:value
#
#怎么读配置文件 ---configparser
(1)实例
from configparser import ConfigParser
#创建对象,(实例化类)
cf = ConfigParser()
#第一步:打开文件 read()
cf.read("lemon.conf",encoding="utf-8")
#第二步读取内容 section 键:option
res = cf.get("section","option")#得到的就是【字符串类型】的值
#res = cf["section"]["option"]#第二种方法读值,返回【字符串类型】
(2)写入配置文件
if not self.cp.has_section(section): # 检查section是否存在 若存在跳过if
self.cp.add_section(section) # 若不存在就创建一个section
self.cp[section][option] = value # 写入一个option=value
with open(self.file_name, "w", encoding="utf-8") as f:
self.cp.write(f) # 保存配置文件
(2)其他方法
#res = cf.getint("section","option")#返回【int类型】的值
#res = cf.getfloat("section","option")#返回【浮点数类型】的值getboolean
#res = cf.getboolean("section","option")#返回【布尔类型】的值
#eval()把括号里面的数据转换成原本的数据类型
#cf.read(filename):读取文件内容
#cf.sections():得到所有的section,并且以列表形式返回
#cf.options(section):得到section下所有的option
#cf.items(option):得到该section所有的键值对
#cf.get(section,option):得到section中option的值,返回string类型的结果
#cf.has_section(section) # 判断section是否存在,若存在则返回True,不存在则返回False
#cf.has_option("section", "option") # 判断option是否存在,若存在则返回True,不存在则返回False
#cf.add_section("section") # 创建一个section
#cf.write(open("file_name","w",encoding="utf-8")) #保存
#cf.remove_section("section") #整个section下的所有内容都将删除
#--------------------------------------------------------------------------------------------------#
12.logging 日志模块 #Python自带模块
# DEBUG 最详细的日志信息,典型应用场景是 问题诊断
# INFO 信息详细程度仅次于DEBUG,通常只记录关键节点信息,用于确认一切都是按照我们预期的那样进行工作
# WARNING 当某些不期望的事情发生时记录的信息(如,磁盘可用空间较低),但是此时应用程序还是正常运行的
# ERROR 由于一个更严重的问题导致某些功能不能正常运行时记录的信息
# CRITICAL 当发生严重错误,
# 字段/属性名称 使用格式 描述
# asctime %(asctime)s 将日志的时间构造成可读的形式,默认情况下是‘2016-02-08 12:00:00,123’精确到毫秒
# name %(name)s 所使用的日志器名称,默认是'root',因为默认使用的是 rootLogger
# filename %(filename)s 调用日志输出函数的模块的文件名; pathname的文件名部分,包含文件后缀
# funcName %(funcName)s 由哪个function发出的log, 调用日志输出函数的函数名
# levelname %(levelname)s 日志的最终等级(被filter修改后的)
# message %(message)s 日志信息, 日志记录的文本内容
# lineno %(lineno)d 当前日志的行号, 调用日志输出函数的语句所在的代码行
# levelno %(levelno)s 该日志记录的数字形式的日志级别(10, 20, 30, 40, 50)
# pathname %(pathname)s 完整路径 ,调用日志输出函数的模块的完整路径名,可能没有
# process %(process)s 当前进程, 进程ID。可能没有
# processName %(processName)s 进程名称,Python 3.1新增
# thread %(thread)s 当前线程, 线程ID。可能没有
# threadName %(thread)s 线程名称
# module %(module)s 调用日志输出函数的模块名, filename的名称部分,不包含后缀即不包含文件后缀的文
件名
# created %(created)f 当前时间,用UNIX标准的表示时间的浮点数表示; 日志事件发生的时间--时间戳,就是
当时调用time.time()函数返回的值
# relativeCreated %(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数; 日志事件发生的时间相对于logging
模块加载时间的相对毫秒数
# msecs %(msecs)d 日志事件发生事件的毫秒部分。logging.basicConfig()中用了参数datefmt,将会去掉
asctime中产生的毫秒部分,可以用这个加上
(1)模块用法
import logging
# 日法收器 Logger 默认日志收集器 root Logger
# 日志输出桌道 handlers 控制台 console file txt test。log
# 日志收集器级别 DEBUG,INFO,WARNING,ERROR,CRITICAL
# 1:定义一个日收集器且还要设置级别 getLogger
my_logger = logging.getLogger("python22")
my_logger.setLevel('DEBUG')
# 2:指定输出渠道还要设置级别 StreamHandLer--为输出到控制台 FiLeHandLer 为输出到指定文件
formatter = logging.Formatter("[%(asctime)s]-[%(name)s]-[%(levelname)s]-[日志信息]:%(message)s")#定义日志格式变量
ch = logging.StreamHandler() # 控制台渠道
ch.setLevel('INFO') # 输出到控制台的级别#输出默认级别为warning
ch.setFormatter(formatter) # 设置日志格式
fh = logging.FileHandler("test.log" , encoding = "utf-8") # 文件渠道
fh.setLevel("INFO") # 输出到文件的级别#输出默认级别为warning
fh.setFormatter(formatter) # 设置日志格式
# 3:对接
my_logger.addHandler(ch) # 对接控制台渠道
my_logger.addHandler(fh) # 对接文件渠道
my_logger.debug("最详细的日志信息")
my_logger.info("记录关键节点信息")
my_logger.warning("某些不期望的事情发生时记录的信息")
my_logger.error("由于一个更严重的问题导致某些功能不能正常运行时记录的信息")
my_logger.critical("当发生严重错误,导致应用程序不能继续运行时记录的信息")
my_logger.removeHandler(ch)#清除控制台渠道信息
my_logger.removeHandler(fh)#清除文件渠道信息
(2)log日志的记录规则
http://blog.chinaunix.net/uid-664509-id-3023200.html
(3)封装成类,方便以后使用
#--------------------------------------------------------------------------------------------------#
13.unittest 单元测试模块
(1)写测试用例
class TestAdd(unittest.TestCase):#先要继承unittest模块中的TestCase类
def test_01_add(self):
self.assertEqual(add(1,2,3),6,"没通过")#断言
def test_02_add(self):
self.assertEqual(add(1,2),3)
def setUpClass(self):#这个是在用例执行前要执行的程序,通常用来加载固件
print("开始执行用例")
def tearDownClass(self):#这个是在用例执行完毕后要执行的程序,通常用来关闭加载的固件。
print("执行结束")
unittest.main()#用来运行程序放在程序入口
(2)加载测试用例和创建测试包
#加载方法1
import unittest
from __0918作业unittest单元测试__ import test_add
def test_add_suite(): #建一个函数
loader = unittest.TestLoader()#实例化TestLoader类,该类是用来加载测试用例
suite = unittest.TestSuite()#实例化TestSuite类,该类是用来创建测试包,接收加载的测试用例
tests = loader.loadTestsFromModule(test_add) #1.通过测试类所在模块来加载测试用例
#loader.loadTestsFromTestCase(test_add) #2.通过测试类来加载测试用例,两种方法选其一
suite.addTest(tests)#将加载好的测试用例装入测试包
return suite#返回测试包
#加载方法2
import os
import unittest
# 自动发现测试用例,直接加载文件夹里的所有测试用例
def test_add_suite():
loader = unittest.TestLoader()# 初始化 loader
# 自动发现测试用例
start_dir = os.path.dirname(os.path.abspath(__file__))#文件夹路径,单元测试用例文件必须得是test开头的才能发现,或者可以改库文件。非用例文件不要以test开头否则容易被重复执行
suite = loader.discover(start_dir)#放入路径
return suite
(3)执行测试用例,将测试用例的执行结果放到控制台
import unittest
import time
import HTMLTestRunnerNew
from __0918作业unittest单元测试__.test_add_suite import test_add_suite
#执行方法1:将测试用例直接输出到控制台
if __name__ == '__main__':
runner = unittest.TextTestRunner(verbosity=2)#测试运行器,
参数verbosity=0,1,2分别代表输出的信息详细级别2为最详细
参数stream=f 将信息输出到文件,默认为输出到控制台
runner.run(test_add_suite())#执行测试用例
#执行方法2:将测试用例输出到文件
if __name__ == '__main__':
# with open("demo1.txt","w",encoding="utf-8") as f:
# runner = unittest.TextTestRunner(verbosity=2,stream=f)
# runner.run(test_add_suite())
#执行方法3:生成HTML测试报告#需要下载HTMLTestRunnerNew模块
if __name__ == '__main__':
name_html = "测试报告" + str(time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime()))+".html"
#文件夹名字根据时间动态生成
with open(name_html, "wb") as f:
runner = HTMLTestRunnerNew.HTMLTestRunner(stream=f,
title='测试报告',
description='这是一个执行加法测试用例的测试报告!',
verbosity=2,
tester='chen')
runner.run(test_add_suite())
(4)其他用法
1.跳过执行用例:
使用 skipXxx 装饰器来跳过测试用例。
unittest 一共提供了 3 个装饰器,分别是 :
@unittest.skip(reason)、
@unittest.skipIf(condition, reason) 、
@unittest.skipUnless(condition, reason)。
其中 skip 代表无条件跳过,skipIf 代表当 condition 为 True 时跳过;
skipUnless 代表当 condition 为 False 时跳过。
使用TestCase 的 skipTest() 方法来跳过测试用例。
#示范1:
@unittest.skip('临时跳过test_02_add')
def test_02_add(self):
self.assertEqual(add(1,2),3)
#示范2:
def test_02_add(self):
self.skipTest('临时跳过test_02_add')
self.assertEqual(add(1,2),3)
2.前置条件和后置条件
def setUpClass(cls):
pass
print("在所有用例执行前执行,只执行一次")
def tearDownClass(cls):
pass
print("在所有用例执行后执行,只执行一次")
def setUp(self):
pass
print("在每个用例执行前执行")
def tearDown(self):
pass
print("在每个用例执行后执行")
(5)拓展,自动生成文件夹,以及生成动态文件名
# 测试报告,放到一个 report 文件夹
# 2,时间动态的生成一个文件名
import os
from datetime import datetime
report_dir = os.path.join(start_dir, 'report')
if not os.path.exists(report_dir):#判断文件夹是否存在,不存在就创建
os.mkdir(report_dir)
time_str = datetime.now().strftime('%Y%m%d%H%M%S')#生成动态文件名(时间动态)
# report/2019-09-18-20.html
file_name = os.path.join(report_dir, time_str + '.html')
#把file_name变量传入即可
(5)断言
断言方法 检查条件
assertEqual(a, b) a == b
assertNotEqual(a, b) a != b
assertTrue(x) bool(x) is True
assertFalse(x) bool(x) is False
assertIs(a, b) a is b
assertIsNot(a, b) a is not b
assertIsNone(x) x is None
assertIsNotNone(x) x is not None
assertIn(a, b) a in b
assertNotIn(a, b) a not in b
assertlsInstance(a, b) isinstance(a, b)
assertNotIsInstance(a, b) not isinstance(a, b)
(6)详细参考文档链接
断言链接:https://blog.csdn.net/qq_29699799/article/details/79947882
https://blog.csdn.net/qq1124794084/article/details/51668672
模块用法:http://c.biancheng.net/view/2679.html
#--------------------------------------------------------------------------------------------------#
14.ddt模块,装饰器,只能和unittest搭配使用
import unittest
from ddt import ddt, data, unpack
from __0918作业unittest单元测试__.add import *
date_dick = [{"a": 1, "b": 1, "c": 2},
{"a": 2, "b": 2, "c": 4},
{"a": 3, "b": 3, "c": 6},
{"a": 4, "b": 4, "c": 8},
{"a": 5, "b": 5, "c": 10}]
@ddt # 装饰器,装饰类用
class TestAdd(unittest.TestCase):
@data(*date_dick) # 装饰类方法,加*是为了解包去掉一层[]
#@unpack # 迭代,在去掉一层[],这里个函数里不用一般传嵌套列表时用到装饰器
def test_01_add(self, dick):
print(str(dick["c"]))
self.assertEqual(add(dick["a"], dick["b"]), dick["c"])
def setUp(self):
print("开始执行用例")
def tearDown(self):
print("执行结束")
if __name__ == "__main__":
unittest.main()
#--------------------------------------------------------------------------------------------------#
15.mock 数据生成,造假数据
(1)生成静态数据
from mock import Mock
add=Mock(return_value=8)
res = add(1,2) #此时res=8
(2)生成动态数据
def demo(a,b)
return a+b
add = Mock(side_effect=demo)
res = add(3,4) # 此时res=a+b
#--------------------------------------------------------------------------------------------------#
16.pytest测试框架,安装pip install pytest
(1)编写用例上:
unittest: 测试类 继承 unittest.TestCase
pytest: 函数/类(Test)里面的方法 test_开头
(2)用例收集上:
unittest: testsuite装用例 ,运行.
pytest: 自动识别用例.
识别用例的规则(编写pytest用例的规则):
1) 根目录:你在哪个目录下运行pytest,它就以哪作为根目录.
2) 文件:命名:test_*.py 或者 *test.py
3) 函数:类: 用例命名:test_开头. Test开头的类(没有__init_)下的test_开头的方法.
(3)用例执行顺序
1) 按照文件排序(ascii): 先识别到的文件,里面的用例先执行.
2) 文件内部-按照代码先后顺序.