测试知识体系五

181 阅读32分钟

5 Python 编程语言与测试框架

名称相关知识点
5.1 Python环境搭建、多版本安装Mac/Windows环境搭建、IDE日常使用
5.2 基本数据类型与操作python 的数字、字符串、列表的使用
5.3 控制流语法条件、循环等表达式与流程控制
5.4 常用数据结构列表、元组、集合、词典与常用便捷表达式
5.5 模块项目目录结构、模块定义、文件引用
5.6 输入与输出字面量打印与格式化、文件读取、json 格式转换
5.7 错误与异常语法错误与定位、异常捕获、异常处理、自定义异常
5.8 面向对象编程类定义、方法定义、类变量、实例引用、实例变量
5.9 标准库os 与文件处理、科学计算、网络访问、日期与时间等处理
5.10 多线程处理进程与多线程处理,log 处理
5.11 第三方库pytest、requests
5.12 pip 依赖管理与虚拟环境第三方的依赖管理与项目管理
5.13 unittestpython 自带单元测试框架
5.14 pytestpython 最流行的全能型测试框架

5.2基本数据类型

Python有五个标准的数据类型:

  • Numbers(数字)
  • String(字符串)
  • List(列表)
  • Tuple(元组)
  • Dictionary(字典)

不可变数据(3 个):Number(数字)、String(字符串)、Tuple(元组);

可变数据(3 个):List(列表)、Dictionary(字典)、Set(集合)。

Python 数字

数字数据类型用于存储数值。不可改变的数据类型

Python支持四种不同的数字类型:

  • int(有符号整型)
  • long(长整型,也可以代表八进制和十六进制)
  • float(浮点型)
  • complex(复数)

注意: long 类型只存在于 Python2.X 版本中,在 2.2 以后的版本中,int 类型数据溢出后会自动转为long类型。在 Python3.X 版本中 long 类型被移除,使用 int 替代。


Python字符串

字符串或串(String)是由数字、字母、下划线组成的一串字符。

s = "a1a2···an"   # n>=0

它是编程语言中表示文本的数据类型。

[头下标:尾下标] 获取的子字符串包含头下标的字符,但不包含尾下标的字符。

>>> s = 'abcdef'
>>> s[1:5]
'bcde'

Python列表

List(列表) 用 [ ] 标识,是 python 最通用的复合数据类型。

  • List 写在方括号之间,元素用逗号隔开,可以是不同类型的元素,通过索引获取元素
  • 和字符串一样,List 可以被索引和切片。
  • List 可以使用 + 操作符进行拼接。
  • List 中的元素是可以改变的。
  • 列表可以作为一个堆栈来使用,后进先出,append() 方法可以把一个元素添加到堆栈顶, pop() 方法可以把一个元素从堆栈顶释放出来
list = [] ## 空列表
list.append('Google') ## 使用 append()整体追加,insert插入指定位置,extend列表添加到列表中
list1 = ['physics', 'chemistry', 1997, 2000] 
del list1[2] #pop删除元素并返回该元素,remove根据元素值删除,如果值不存在就报错 

Python包含以下函数:

序号函数
1cmp(list1, list2) 比较两个列表的元素
2len(list) 列表元素个数
3max(list) 返回列表元素最大值
4min(list) 返回列表元素最小值
5list(seq) 将元组转换为列表

Python包含以下方法:

序号方法
1list.append(obj) 在列表末尾添加新的对象
2list.count(obj) 统计某个元素在列表中出现的次数
3list.extend(seq) 在列表末尾一次性追加另一个序列中的多个值(用新列表扩展原来的列表)
4list.index(obj) 从列表中找出某个值第一个匹配项的索引位置
5list.insert(index, obj) 将对象插入列表
6list.pop([index=-1]) 移除列表中的一个元素(默认最后一个元素),并且返回该元素的值
7list.remove(obj) 移除列表中某个值的第一个匹配项
8list.reverse() 反向列表中元素
9list.sort(cmp=None, key=None, reverse=False) 对原列表进行排序

Python 元组

元组用 () 标识。内部元素用逗号隔开。但是元组不能二次赋值,相当于只读列表。

  • 与字符串一样,元组的元素不能修改。
  • 元组也可以被索引和切片,方法都是一样的。
  • 注意构造包含 0 或 1 个元素的元组的特殊语法规则。
  • 元组也可以使用 + 操作符进行拼接。
  • 元组中的元素值是不允许删除的,但我们可以使用del语句来删除整个元组
  • 对元组进行连接组合,如下实例:
tup1 = (12, 34.56) 

tup2 = ('abc', 'xyz') 

tup3 = tup1 + tup2 

print tup3

删除元组

元组中的元素值是不允许删除的,但我们可以使用del语句来删除整个元组

del tup


Python 字典

列表是有序的对象集合,字典是无序的对象集合。两者之间的区别在于:字典当中的元素是通过键来存取的,而不是通过偏移存取。字典用"{ }"标识。字典由索引(key)和它对应的值value组成。

  • 键必须是唯一的,但值则不必,通过键获取元素
  • 不允许同一个键出现两次。创建时如果同一个键被赋值两次,后一个值会被记住

tinydict.clear() # 清空字典所有条目 

del tinydict # 删除字典

Python数据类型转换

有时候,我们需要对数据内置的类型进行转换,数据类型的转换,你只需要将数据类型作为函数名即可。

以下几个内置的函数可以执行数据类型之间的转换。这些函数返回一个新的对象,表示转换的值。

函数描述
int(x [,base])将x转换为一个整数
float(x)将x转换到一个浮点数
complex(real [,imag])创建一个复数
str(x)将对象 x 转换为字符串
tuple(s)将序列 s 转换为一个元组
list(s)将序列 s 转换为一个列表
set(s)转换为可变集合
dict(d)创建一个字典。d 必须是一个序列 (key,value)元组。

split 函数

split函数可以将字符串按照一定规则进行切割成列表,默认按照空格进行切割,如果字符串无空格则直接将这个字符串变为列表中的一个元素,还可以传入切割次数,默认-1无限制

str_01 = 'abc'
# 默认按照空格切割,无空格则整个转换为列表中的一个元素
print(str_01.split())

str_02 = 'a b c'
# 默认按照空格切割
print(str_02.split())

str_03 = 'a#b#c'
# 指定按照#切割
print(str_03.split('#'))

str_04 = 'stark0peter0strange0banner'
# 切割1次
print(str_04.split('0', 1))

str_05 = 'pc12138'
print(str_05.split(''))
复制代码

image.png

5.5模块

项目目录结构、模块定义

模块是一个包含所有你定义的函数和变量的文件,其后缀名是.py。模块可以被别的程序引入,以使用该模块中的函数等功能。

__name__属性

可以用__name__属性来使该程序块仅在该模块自身运行时执行。

#!/usr/bin/python3
# Filename: using_name.py

if __name__ == '__main__':
   print('程序自身在运行')
else:
   print('我来自另一模块')

运行输出如下:

$ python using_name.py
程序自身在运行
$ python
>>> import using_name
我来自另一模块
>>>

说明:  每个模块都有一个__name__属性,当其值是'main'时,表明该模块自身在运行,否则是被引入。

说明: name  与  main  底下是双下划线, _ _ 是这样去掉中间的那个空格。

文件引用

open() 方法

Python open() 方法用于打开一个文件,并返回文件对象。

注意: 使用 open() 方法一定要保证关闭文件对象,即调用 close() 方法。

open() 函数常用形式是接收两个参数:文件名(file)和模式(mode)。

完整的语法格式为:

open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

参数说明:

  • file: 必需,文件路径(相对或者绝对路径)。
  • mode: 可选,文件打开模式
  • buffering: 设置缓冲
  • encoding: 一般使用utf8
  • errors: 报错级别
  • newline: 区分换行符
  • closefd: 传入的file参数类型
  • opener: 设置自定义开启器,开启器的返回值必须是一个打开的文件描述符。

mode 参数有:

模式描述
t文本模式 (默认)。
x写模式,新建一个文件,如果该文件已存在则会报错。
b二进制模式。
+打开一个文件进行更新(可读可写)。
U通用换行模式(Python 3 不支持)。
r以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
rb以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。一般用于非文本文件如图片等。
r+打开一个文件用于读写。文件指针将会放在文件的开头。
rb+以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。一般用于非文本文件如图片等。
w打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
wb以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
w+打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
wb+以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
a打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
ab以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
a+打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
ab+以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。

默认为文本模式,如果要以二进制模式打开,加上 b 。

file 对象

file 对象使用 open 函数来创建,下表列出了 file 对象常用的函数:

序号方法及描述
1file.close()关闭文件。关闭后文件不能再进行读写操作。
2file.flush()刷新文件内部缓冲,直接把内部缓冲区的数据立刻写入文件, 而不是被动的等待输出缓冲区写入。
3file.fileno()返回一个整型的文件描述符(file descriptor FD 整型), 可以用在如os模块的read方法等一些底层操作上。
4file.isatty()如果文件连接到一个终端设备返回 True,否则返回 False。
5file.next()**Python 3 中的 File 对象不支持 next() 方法。**返回文件下一行。
6file.read([size])从文件读取指定的字节数,如果未给定或为负则读取所有。
7file.readline([size])读取整行,包括 "\n" 字符。
8file.readlines([sizeint])读取所有行并返回列表,若给定sizeint>0,返回总和大约为sizeint字节的行, 实际读取值可能比 sizeint 较大, 因为需要填充缓冲区。
9file.seek(offset[, whence])移动文件读取指针到指定位置
10file.tell()返回文件当前位置。
11file.truncate([size])从文件的首行首字符开始截断,截断文件为 size 个字符,无 size 表示从当前位置截断;截断之后后面的所有字符被删除,其中 windows 系统下的换行代表2个字符大小。
12file.write(str)将字符串写入文件,返回的是写入的字符长度。
13file.writelines(sequence)向文件写入一个序列字符串列表,如果需要换行则要自己加入每行的换行符。

5.6Json字符串转换

Python3 中可以使用 json 模块来对 JSON 数据进行编解码,它包含了两个函数:

  • json.dumps(): 对数据进行编码。
  • json.loads(): 对数据进行解码。

Python 编码为 JSON 类型转换对应表:

PythonJSON
dictobject
list, tuplearray
strstring
int, float, int- & float-derived Enumsnumber
Truetrue
Falsefalse
Nonenull

JSON 解码为 Python 类型转换对应表:

JSONPython
objectdict
arraylist
stringstr
number (int)int
number (real)float
trueTrue
falseFalse
nullNone

执行以上代码输出结果为:

#!/usr/bin/python3 
import json # Python 字典类型转换为 JSON 对象 
data = { 
    'no' : 1, 
    'name' : 'Runoob', 
    'url' : 'http://www.runoob.com' 
    } 
json_str = json.dumps(data) 
print ("Python 原始数据:", repr(data)) 
print ("JSON 对象:", json_str)


Python 原始数据: {'url': 'http://www.runoob.com', 'no': 1, 'name': 'Runoob'}
JSON 对象: {"url": "http://www.runoob.com", "no": 1, "name": "Runoob"}
#!/usr/bin/python3 
import json # Python 字典类型转换为 JSON 对象 
data1 = { 
        'no' : 1, 
        'name' : 'Runoob', 
        'url' : 'http://www.runoob.com' 
        } 
json_str = json.dumps(data1) 
print ("Python 原始数据:", repr(data1)) 
print ("JSON 对象:", json_str) 
# 将 JSON 对象转换为 Python 字典 
data2 = json.loads(json_str) 
print ("data2['name']: ", data2['name']) 
print ("data2['url']: ", data2['url'])


Python 原始数据: {'name': 'Runoob', 'no': 1, 'url': 'http://www.runoob.com'}
JSON 对象: {"name": "Runoob", "no": 1, "url": "http://www.runoob.com"}
data2['name']:  Runoob
data2['url']:  http://www.runoob.com

5.7 错误与异常

  • 首先,执行 try 子句(在关键字 try 和关键字 except 之间的语句)

  • 如果没有异常发生,忽略 except 子句,try 子句执行后结束。

  • 如果在执行 try 子句的过程中发生了异常,那么 try 子句余下的部分将被忽略。

  • 如果异常的类型和 except 之后的名称相符,那么对应的 except 子句将被执行。最后执行 try 语句之后的代码。

  • 如果一个异常没有与任何的 except 匹配,那么这个异常将会传递给上层的 try 中。

  • 一个 try 语句可能包含多个 except 子句,分别来处理不同的特定的异常。最多只有一个分支会被执行。

  • 可选的 else 子句,必须放在所有的 except 子句之后,这个子句将在 try 子句没有发生任何异常的时候执行

  • 一个 except 子句可以同时处理多个异常,这些异常将被放在一个括号里成为一个元组

      except (RuntimeError, TypeError, NameError): 
      pass
    
  • try-finally 语句无论是否发生异常都将执行最后的代码。

常见异常

AttributeError,对象不含指定属性的异常

class Student:
    name = None
    age = 7

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return 'Student[name={}, age={}]'.format(self.name, self.age)

stu = Student("stark", 44)
print(stu)
print(stu.address)

image.png

KeyError,没有指定的键出现的异常

dict_01 = {
    'name': 'stark',
    'age': 44
}

print(dict_01['address'])

image.png

IndexError,数组下标异常

list_01 = [1, 3, 4, 9, 10]
list_01[10]

image.png

ValueError,参数值异常

name = 'stark'
print(int(name))

image.png

TypeError,参数类型异常

def add(x, y):
    return x + y

add()

image.png

5.8面向对象

  • 类:用来描述具有相同属性和方法的对象的集合,定义了结合中每个对象所共有的属性和方法,对象是类的实例
  • 方法:类中定义的函数
  • 实例变量:也叫对象变量,讲一个对象的改变,不改变其他对象的值
  • 类变量:也叫静态变量,static修饰,类变量定义在类中且在函数体之外,其中一个对象将值改变,其他对象得到都是改变后的

image.png

class GirlFriend:
    # 女朋友类
    gender = "女"
 
    # 对象方法(实例方法):
    # 实例方法的定义:直接定义在类里面的函数
    # 第一个参数是self,self代表的是对象本身
    # 实例方法的调用:对象.方法名
    def __init__(self, name, face, height, leg):# 初始化方法(对象方法)
        self.name = name # 实列属性
        self.face = face
        self.height = height
        self.leg = leg
        # 对象.属性名 = 属性值
 
    def skill1(self):
        # print(self)
        print("{} 在买东西".format(self.name))
 
    def skill2(self):
        print("看电视")
 
    # 类方法:
    # 类方法定义:要通过classmethod装饰器来声明一个类方法
    # 第一个参数是cls,cls代表的是类本身
    # 类方法的调用:类名.方法名
    # 对象.方法名
    @classmethod  # 通过classmethod装饰器,声明一个类方法
    def cls_func(cls):
        #print(cls)
        print("这个是类方法")
 
obj1 = GirlFriend('小花', "好看", 168, "一米")
obj2 = GirlFriend('小红', "很好看", 168, "一米一")
 
print("----------------------------")
# 类方法的调用
print(GirlFriend)
GirlFriend.cls_func()
obj1.cls_func()
 
print("----------------------------")
# 实例方法的调用
obj1.skill1()
 
# 类不能够去调用实例方法的
# GirlFriend.skill1()
  • 方法重写:如果父类的功能无法满足需求,可以在子类中重写父类的方法
Class Parent:
    def mythod(self):
        print('调用父类方法')
Class Child(Parent):
    def mythod(self):
        print('调用子类方法')
        
c = Child()
c.mythod()
super(Child,c).mythod()

输出:
调用子类方法
调用父类方法

高级函数

str对象描述信息的定义函数

class Student():

    def __init__(self, name):
        self.name = name
    # 定义实例化对象的描述信息
    def __str__(self):
        return 'Student[name={}]'.format(self.name)


    def breath(self):
        print('Student can breath')

if __name__ == '__main__':
    stu = Student('子渊')
    print(stu)
    stu.breath()

image.png

getattr当调用属性或方法不存在时,返回函数中定义的信息

class Student():

    def __init__(self, name):
        self.name = name

    def __str__(self):
        return 'Student[name={}]'.format(self.name)

    def __getattr__(self, property):
        print('{}不存在'.format(property))

    def breath(self):
        print('Student can breath')

if __name__ == '__main__':
    stu = Student('子渊')
    print(stu)
    stu.breath()
    stu.age

image.png

setattr拦截当前类中不存在的属性和值,给不存在的属性设置值

def __setattr__(self, key, value):
    if key not in self.__dict__:
        self.__dict__[key] = value
    print('key={}, value={}'.format(key, value))

在main函数下增加代码

stu.like = 'lilith'
stu.like

image.png

5.9标准库

OS

序号方法及描述
1os.access(path, mode) 检验权限模式
2os.chdir(path) 改变当前工作目录
3os.chflags(path, flags) 设置路径的标记为数字标记。
4os.chmod(path, mode) 更改权限
5os.chown(path, uid, gid) 更改文件所有者
6os.chroot(path) 改变当前进程的根目录
7os.close(fd) 关闭文件描述符 fd
9os.dup(fd) 复制文件描述符 fd
10os.dup2(fd, fd2) 将一个文件描述符 fd 复制到另一个 fd2
11os.fchdir(fd) 通过文件描述符改变当前工作目录
12os.fchmod(fd, mode) 改变一个文件的访问权限,该文件由参数fd指定,参数mode是Unix下的文件访问权限。
21os.getcwd() 返回当前工作目录
33os.makedirs(path[, mode]) 递归文件夹创建函数。像mkdir(), 但创建的所有intermediate-level文件夹需要包含子文件夹。
35os.mkdir(path[, mode]) 以数字mode的mode创建一个名为path的文件夹.默认的 mode 是 0777 (八进制)。
36os.mkfifo(path[, mode]) 创建命名管道,mode 为数字,默认为 0666 (八进制)
38os.open(file, flags[, mode]) 打开一个文件,并且设置需要的打开选项,mode参数是可选的
40os.pathconf(path, name) 返回相关文件的系统配置信息。
41os.pipe() 创建一个管道. 返回一对文件描述符(r, w) 分别为读和写
44os.readlink(path) 返回软链接所指向的文件
45os.remove(path) 删除路径为path的文件。如果path 是一个文件夹,将抛出OSError; 查看下面的rmdir()删除一个 directory。
46os.removedirs(path) 递归删除目录。
47os.rename(src, dst) 重命名文件或目录,从 src 到 dst
48os.renames(old, new) 递归地对目录进行更名,也可以对文件进行更名。
49os.rmdir(path) 删除path指定的空目录,如果目录非空,则抛出一个OSError异常。

标准库概述

文件通配符

glob模块提供了一个函数用于从目录通配符搜索中生成文件列表

import glob

glob.glob('*.py')

输出:['primes.py','random.py','quote.py']

命令行参数

import sys
print(sys.argv)

输出:['demo.py','one','two']

正则匹配

import re
re.findall(r'\bf[a-z]*','which foot or hand fell faster')

输出:['foot','fell','faster']

数学

import math
math.cos(math.pi/4)

import random
random.randrange(6)

日期和时间

from datetime import date
now = date.today()
birthday = data(1964,7,31)
age = now -birthday
age.days

测试模块

import doctest
doctest.testmod()   #自动验证嵌入测试

import unittest
class TestStaticalFunctions(unittest.TestCase):
    def test_average(self):
        self.assertEqual(average([20,30,70]),40.0)
        self.assertEqual(round(average([20,30,70]),1),4.3)
        self.assertRaises(ZeroDivisionError,average,[])
        self.assertRaises(TypeError,average,20,30,70)
unittest.main()

5.10多线程处理

优点

  • 使用线程可以把占据长时间的程序任务放到后台处理
  • 用户界面更加吸引人,可以弹出进度条来显示处理的进度
  • 程序运行速度可能加快
  • 在一些等待的任务实现上,可以释放一些珍贵的资源如内存占用等等

python线程

_thread.start_new_thread ( function ,args[,kwargs])

function:线程函数
args:传递给线程函数的参数,必须是tuple
kwargs:可选参数

image.png

线程同步

image.png

5.11第三方库

requests

响应信息

属性或方法说明
apparent_encoding编码方式
close()关闭与服务器的连接
content返回响应的内容,以字节为单位
cookies返回一个 CookieJar 对象,包含了从服务器发回的 cookie
encoding解码 r.text 的编码方式
headers返回响应头,字典格式
history返回包含请求历史的响应对象列表(url)
is_permanent_redirect如果响应是永久重定向的 url,则返回 True,否则返回 False
is_redirect如果响应被重定向,则返回 True,否则返回 False
iter_content()迭代响应
json()返回结果的 JSON 对象 (结果需要以 JSON 格式编写的,否则会引发错误)
links返回响应的解析头链接
next返回重定向链中下一个请求的 PreparedRequest 对象
ok检查 "status_code" 的值,如果小于400,则返回 True,如果不小于 400,则返回 False
raise_for_status()如果发生错误,方法返回一个 HTTPError 对象
reason响应状态的描述,比如 "Not Found" 或 "OK"
request返回请求此响应的请求对象
status_code返回 http 的状态码,比如 404 和 200(200 是 OK,404 是 Not Found)
text返回响应的内容,unicode 类型数据
url返回响应的 URL

requests方法

requests 方法如下表:

方法描述
delete(urlargs)发送 DELETE 请求到指定 url
get(urlparams, args)发送 GET 请求到指定 url
head(urlargs)发送 HEAD 请求到指定 url
patch(urldata, args)发送 PATCH 请求到指定 url
post(urldata, json, args)发送 POST 请求到指定 url
put(urldata, args)发送 PUT 请求到指定 url
request(methodurlargs)向指定的 url 发送指定的请求方法

①使用 requests.request() 发送 get 请求:


# 导入 requests 包
import requests
# 发送请求
x = requests.request('get''https://www.runoob.com/')
# 返回网页内容
print(x.status_code)

输出结果如下:

200

②设置请求头:

import requests

kw = {'s':'python 教程'}

# 设置请求头
headers = {"User-Agent""Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}
 
# params 接收一个字典或者字符串的查询参数,字典类型自动转换为url编码,不需要urlencode()
response = requests.get("https://www.runoob.com/", params = kw, headers = headers)

# 查看响应状态码
print (response.status_code)

# 查看响应头部字符编码
print (response.encoding)

# 查看完整url地址
print (response.url)

# 查看响应内容,response.text 返回的是Unicode格式的数据
print(response.text)

输出结果如下:

200
UTF-8
https://www.runoob.com/?s=python+%E6%95%99%E7%A8%8B

... 其他内容...

③post() 方法可以发送 POST 请求到指定 url,一般格式如下:

requests.post(url, data={key: value}, json={key: value}, args)
  • url 请求 url。
  • data 参数为要发送到指定 url 的字典、元组列表、字节或文件对象。
  • json 参数为要发送到指定 url 的 JSON 对象。
  • args 为其他参数,比如 cookies、headers、verify等。
# 导入 requests 包
import requests
# 发送请求
x = requests.post('https://www.runoob.com/try/ajax/demo_post.php')\

# 返回网页内容
print(x.text)

输出结果如下:

<p style='color:red;'>本内容是使用 POST 方法请求的。</p>
<p style='color:red;'>请求时间:
2022-05-26 17:30:47</p>

④post 请求带参数:

import requests

# 表单参数,参数名为 fname 和 lname
myobj = {'fname''RUNOOB','lname''Boy'}

# 发送请求
x = requests.post('https://www.runoob.com/try/ajax/demo_post2.php', data = myobj)

# 返回网页内容
print(x.text)

输出结果如下:

<p style='color:red;'>你好,RUNOOB Boy,今天过得怎么样?</p>

5.11unittest

unittest四个概念

  1. test case:就是我们的测试用例,unittest中提供了一个基本类TestCase,可以用来创建新的测试用例,一个TestCase的实例就是一个测试用例;unittest中测试用例方法都是以test开头的,且执行顺序会按照方法名的ASCII值排序。
  2. test fixure:测试夹具,用于测试用例环境的搭建和销毁。即用例测试前准备环境的搭建(SetUp前置条件),测试后环境的还原(TearDown后置条件),比如测试前需要登录获取token等就是测试用例需要的环境,运行完后执行下一个用例前需要还原环境,以免影响下一条用例的测试结果。
  3. test suite:测试套件,用来把需要一起执行的测试用例集中放到一块执行,相当于一个篮子。我们可以使用TestLoader来加载测试用例到测试套件中。
  4. test runner:用来执行测试用例的,并返回测试用例的执行结果。它还可以用图形或者文本接口,把返回的测试结果更形象的展现出来,如:HTMLTestRunner。

unittest断言

方法检查
assertEqual(a, b,msg=None)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
assertIsInstance(a, b)isinstance(a,b)
assertNotIsInstance(a, b)not isinstance(a,b)

​ 如果断言失败即不通过就会抛出一个AssertionError断言错误,成功则标识为通过,以上几种方式都有一个共同点,就是都有一个msg参数(表中只列了一个,其实都有),默认是None,即msg = None,如果指定msg参数的值,则将该信息作为失败的错误信息返回。

TestCase测试用例

在编写测试用例之前,需要新建一个测试类继承unittest的TestCase类

步骤

  • 导入unittest模块
  • 创建一个测试类,并继承unittest.TestCase()
  • 定义测试方法,方法名必须以test_开头
  • 调用unittest.main()方法来运行测试用例,unittest.main()方法会搜索该模块下所有以test开头的测试用例方法,并自动执行
# register.py
users = [{'username': 'test', 'password': '123456'}]


def register(username, password1, password2):

    if not all([username, password1, password2]):
        return {"code": 0, "msg": "所有参数不能为空"}
    # 注册功能
    for user in users:
        if username == user['username']:
            return {"code": 0, "msg": "该用户名已存在!"}
    else:
        if password1 != password2:
            return {"code": 0, "msg": "两次密码输入不一致!"}
        else:
            if 6 <= len(username) >= 6 and 6 <= len(password1) <= 18:
                users.append({'username': username, 'password': password2})
                return {"code": 1, "msg": "注册成功"}
            else:
                return {"code": 0, "msg": "用户名和密码必须在6-18位之间"}
                
                # test_register.py
import unittest
from register import register   # 导入被测试的代码


class TestRegister(unittest.TestCase):
    """注册接口测试用例类"""

    def test_register_success(self):
        """注册成功"""
        data = ("mikitest", "miki123", "miki123")   # 测试数据
        expected = {"code": 1, "msg": "注册成功"}   # 预期结果
        result = register(*data)    # 把测试数据传到被测的代码,接收实际结果
        self.assertEqual(expected, result)  # 断言,预期和实际是否一致,一致即用例通过

    def test_username_isnull(self):
        """注册失败-用户名为空"""
        data = ("", "miki123", "miki123")
        expected = {"code": 0, "msg": "所有参数不能为空"}
        result = register(*data)
        self.assertEqual(expected, result)

    def test_username_lt6(self):
        """注册失败-用户名大于18位"""
        data = ("mikitestmikitestmikitest", "miki123", "miki123")
        expected = {"code": 0, "msg": "用户名和密码必须在6-18位之间!"}
        result = register(*data)
        self.assertEqual(expected, result)	# 这条用例应该是不通过的,注册代码bug

    def test_pwd1_not_pwd2(self):
        """注册失败-两次密码不一致"""
        data = ("miki123", "test123", "test321")
        expected = {"code": 0, "msg": "两次密码输入不一致!"}
        result = register(*data)
        self.assertEqual(expected, result)


# 如果直接运行这个文件,需要使用unittest中的main函数来执行测试用例
if __name__ == '__main__':
    unittest.main()

TestFixure测试夹具

class TestRegister(unittest.TestCase): 
    """注册接口测试用例类""" 
    def setUp(self): # 每条用例执行之前都会执行 
        print("用例{}开始执行--".format(self)) 
    def tearDown(self): # 每条用例执行之后都会执行 
        print("用例{}执行结束--".format(self)) 
    
    @classmethod # 指明这是个类方法以类为维度去执行的 
    def setUpClass(cls): # 整个测试用例类中的用例执行之前,会先执行此方法 
        print("-----setup---class-----") 
    @classmethod 
    def tearDownClass(cls): # 整个测试用例类中的用例执行完之后,会执行此方法 
        print("-----teardown---class-----")

TestSuite测试套件

  • unittest.TestSuite()

    • addTest():添加单个测试用例方法
    • addTest([..]):添加多个测试用例方法,方法名存在一个列表
  • unittest.TestLoader()

    • loadTestsFromTestCase(测试类名):添加一个测试类
    • loadTestsFromModule(模块名):添加一个模块
    • discover(测试用例的所在目录):指定目录去加载,会自动寻找这个目录下所有符合命名规则的测试用例
import unittest
import test_register

# 第一步,创建一个测试套件
suite = unittest.TestSuite()

# 第二步:将测试用例,加载到测试套件中
# 方式1,添加单条测试用例
case = test_register.TestRegister("test_register_success") # 创建一个用例对象
suite.addTest(case)	# 添加用例到测试套件中

# 方式2,添加多条测试用例
case1 = test_register.TestRegister("test_register_success")
case2 = test_register.TestRegister("test_username_isnull")
# suite.addTest([case1, case2])	# 添加用例到测试套件中

# 方式3,添加一个测试用例类
loader = unittest.TestLoader()	# 创建一个加载对象
suite.addTest(loader.loadTestsFromTestCase(test_register.TestRegister))

# 方式4,添加一个模块
loader = unittest.TestLoader()	# 创建一个加载对象
suite.addTest(loader.loadTestsFromModule(test_register))

# 方式5,指定测试用例的所在的目录路径,进行加载
loader = unittest.TestLoader()
suite.addTest(loader.discover(r"d:\learn\python"))

TestRunner执行用例

​ test runner执行测试用例的,并且可以生成相应的测试报告。测试报告有text文本和html。html格式的是HTMLTestRunner了,HTMLTestRunner是 Python 标准库的 unittest 框架的一个扩展,它可以生成一个直观清晰的 HTML 测试报告。

import unittest
import test_register from HTMLTestRunner import HTMLTestRunner

# 创建测试套件
suite = unittest.TestSuite()

# 通过模块加载测试用例
loader = unittest.TestLoader()
suite.addTest(loader.loadTestsFromModule(test_register))

# 创建测试运行程序启动器
runner = HTMLTestRunner(stream=open("report.html", "wb"),  # 打开一个报告文件,将句柄传给stream
                        tester="miki",                    # 报告中显示的测试人员
                        description="注册接口测试报告",        # 报告中显示的描述信息
                        title="自动化测试报告")                 # 报告的标题

# 使用启动器去执行测试套件里的用例
runner.run(suite)

​ 相关参数说明:

  • stream:指定输出的方式

  • tester:报告中要显示的测试人员的名字

  • description:报告中要显示的面熟信息

  • title:测试报告的标题

  • verbosity :表示测试报告信息的详细程度,一共三个值,默认是2

    • 0 (静默模式):你只能获得总的测试用例数和总的结果,如:总共100个 失败10 成功90
    • 1 (默认模式):类似静默模式,只是在每个成功的用例前面有个. 每个失败的用例前面有个F
    • 2 (详细模式):测试结果会显示每个测试用例的所有相关的信息

python垃圾回收机制

引用计数器

环状双向链表 python中创建的任何对象都会放到refchain双向链表中 image.png

  • 内部会创建一些数据【上一个对象,下一个对象,类型,引用个数】 name='lxq'
    new=name 引用个数加一
  • 内部会创建一些数据【上一个对象,下一个对象,类型,引用个数,value=18】
    age=18
  • 内部会创建一些数据【上一个对象,下一个对象,类型,引用个数,items=元素,元素个数】
    hobby=['swim','sing']

C源码中使用PyObject结构体来体现每个对象都有相同的值{4个值} 多个元素组成的对象:PyObject结构体+ob_size(元素个数)

类型封装结构体

  1. data = 3.14
    内部创建: _ob_next _ob_prev ob_refcnt=1 ob_type=float ob_fval=3.14

引用计数器 python运行时会根据数据类型的不同找到对应的结构体,根据结构体的字段创建相关数据,然后将对象添加到双向链表中

关键结构体PyObject PyVarObject

ob_refcnt就是引用计数器 默认为一,当有变量引用对象,计数器变化

a=100
b=a  #引用
del b  # b变量删除,b对应对象的引用计数器减一
del a  # a变量删除,a对应对象的引用计数器减一
#当一个对象引用计数器为0,进行垃圾回收
# 1.对象从双向链表删除2.对象销毁,内存归还

循环引用

v1=[1,2,3]   #v1引用计数机加一
v2=[4,5,6]   #v2引用计数机加一
v1.append(v2)  #v2引用计数机加一
v2.append(v1)  #v1引用计数机加一
del v1   #v1引用计数机减一
del v2   #v2引用计数减一

标记清除

  • 解决引用计数器循环引用不足,在python底层再维护一个存放可能存在循环引用的对象(list,tuple,dict,set)的链表
  • 某个时刻,扫描可能存在循环引用的链表中的每个元素,检查是否有循环引用,如果有,让双方引用计数器-1,为零则回收 image.png

分代回收

  • 什么时候扫描
  • 可能存在循环引用的链表扫描代价大,耗时久

将可能存在循环引用的链表维护成三个链表:

  • 0代:对象个数达到700个扫描一次
  • 1代:0代扫描十次扫描一次
  • 2代:一代扫描十次扫描一次

image.png

小结

  • 在python中维护了一个refchain的双向环状链表,存储程序中创建的所有对象,每种类型的对象都有一个ob_refcnt引用计数器的值,引用计数器变为零会进行垃圾回收(对象销毁,从refchain中移除)
  • 但是在python中有多个元素组成的对象可能存在循环引用的问题,为了解决这个问题,引入标记清除和分代回收,在其内部维护四个链表(refchain,0代,1代,2代)在源码内部,当达到预值,就会扫描链表,发现循环引用,各自减一

缓存机制

避免重复创建和销毁一些常见对象,维护池

#启动解释器python内部会帮我们创建-5,-4.。。257
v1=7  #不开辟内存,直接去池中获取
v2=9  #不开辟内存,直接去池中获取
v3=9  #不开辟内存,直接去池中获取

free_list 计数为零,应该回收,但内部不会回收,添加到free_list链表中缓存,以后再去创建对象,不开辟内存,而是直接使用free_list

pyetst

6 Web 自动化测试

名称相关知识点
6.1 seleniumselenium简介及实战
6.2 page objectpage object 设计模式详解及实战

7 移动端 app 自动化测试

名称相关知识点
7.1 appiumappium 基础知识及实战
7.2 appium使用技巧元素定位、弹窗识别、webview测试等

8 常用开源测试平台

名称相关知识点
8.1 monkeyandroid 健壮性与压力测试工具 monkey 的进阶使用
8.2 maximandroid 遍历工具
8.3 appcrawler多平台自动遍历测试工具
8.4 STF多设备管理平台 STF 打造自己的智能设备实验室管理上百台设备
8.5 Selenium Grid跨平台设备管理方案 Selenium Grid 构建支持 android、ios、web 的多架构自动化测试平台

9 客户端专项测试

名称相关知识点
9.1 启动性能分析冷启动、热启动、暖启动、首屏启动指标分析
9.2 接口性能分析dns、http/https 的接口性能分析
9.3 Webview性能分析hybrid app 的性能分析
9.4 H5性能分析手机浏览器的性能数据获取与分析
9.5 卡顿分析过度绘制、冰冻帧、卡顿数据
9.6 系统资源分析cpu 统计、mem 统计、网络流量分析
9.7 耗电量测试使用 batterystats 与 battery historian 完成耗电量的基准分析
9.8 弱网测试模拟弱网、丢包、延迟、不可访问等多种条件下的应用体验
9.9 健壮性测试使用 monkey maxim 完成 app 的健壮性测试
9.10 兼容性测试使用 appcrawler 完成遍历与兼容性分析
9.11 代码覆盖率jacoco 代码覆盖率

10 服务端接口测试

名称相关知识点
10.1 常见接口协议tcp/udp/http/restful/dubbo
10.2 抓包分析 tcp 协议使用 tcpdump 与 wireshark 分析三次握手与四次挥手流程
10.3 postman/curlpostman及curl简介及使用
10.4 常用代理工具charles、burpsuite、mitmproxy、anyproxy
10.5 http/https 抓包分析ssl 证书设置与 https 抓包
10.6 http 协议讲解状态码、header、请求与响应的格式分析
10.7 get、postget 与 post 的本质区别与具体抓包解读
10.8 session、cookie、token了解 session、cookie、token

11 服务端接口自动化测试

名称相关知识点
11.1 接口测试框架requests
11.2 接口请求构造get/post/put/head 等 http 请求构造
11.3 接口测试断言状态码、返回内容等断言
11.4 json/xml 请求优雅的发送 json、xml 请求
11.5 json/xml 响应断言json path、xpath 进行断言
11.6 schema 断言大量响应数据字段的格式断言
11.7 header cookieheader 自定义与 cookie 复用
11.8 认证体系http basic、oauth2 等认证体系的测试

12 服务端性能测试

名称相关知识点
12.1 JMeterJMeter 实战
12.2 性能监控系统influxdb、grafana、prometheus 实战

13 接口安全测试

名称相关知识点
13.1 服务端安全测试体系详解 OWASP 的 top10 安全漏洞与安全防护体系
13.2 安全测试演练环境搭建安全测试演练环境实操常见安全漏洞
13.3 常见接口安全测试工具zap、burpsuite、sqlmap 等知名安全测试工具介绍
13.4 BurpSuite黑客与白帽子最常用的安全测试工具详解
13.5 命令注入漏洞命令注入漏洞原理与实操
13.6 sql 注入漏洞sql 注入、sql 盲注等漏洞的原理介绍与实操
13.7 xss 漏洞xss 多种漏洞的原理介绍与实操
13.8 csrf 漏洞csrf 漏洞原理介绍与实操