目录

基础内容

元组/集合/列表/字典

​编辑列表

元组

字典

集合

文件操作

打开文件/关闭文件

读取文件

写入内容

三种打开方式(w+,r+,a+)

python操作json文件

json文件介绍

读取

写入

pytest

分目录管理

测试用例编写执行

断言

参数化

生成测试报告

前后置

allure生成测试报告

YAML管理数据

pytest实例

登录接口实例

本文介绍了python中的部分语法和相关容器,以及文件操作(json)部分的内容(涉及重点为三种打开方式a+ 、r+、w+),为自动化测试打下基础,最后介绍了pytest的相关语法。

基础内容

元组/集合/列表/字典

四种容器特性:


列表

常用方法:

insert、append、remove、pop、index、count、reverse、sort

#列表定义  可变
my_list = [1,6,3,4,5,6,2,8,9]
#增删改元素 insert append remove pop index索引切片 count出现次数 reserve反转 sort排序

#insert 插入元素
my_list.insert(0,10) #在第一个元素前插入10
print(my_list)
#append 尾部追加元素
my_list.append(10)
print(my_list)
#删除指定元素
my_list.remove(10) #删除元素10(第一次出现)
print(my_list)
#删除最后一个元素
my_list.pop()
print(my_list)
#索引切片index
print(my_list.index(3)) #返回3的位置(第一次出现)
print(my_list.count(1)) #1出现的次数
#反转列表元素
my_list.reverse()
print(my_list)
#sort排序
my_list.sort()
print(my_list) #默认升序

for item in my_list:
    print(item)#这里的item便是列表中的每一个元素

元组

元组一旦创建不可修改,不可以增删改元素

len、max、min、sum、count、index

#元组
my_tuple=(1,6,7,3,2,6,1,7,9)

#序列通用操作 len max min sum + *n x in tup count index
#统计出现次数count
print(my_tuple.count(1))
#index出现位置
print(my_tuple.index(6))
#长度,元素个数
print(len(my_tuple))
#max min sum
print(max(my_tuple))
print(min(my_tuple))
print(sum(my_tuple))
#长度延申两倍
print(my_tuple*2)

字典

keys、values、items、list、get、pop、popitem、clear、update

#字典
my_dict={"name":"Test","age":24,"Job":"Test"} #键不可以重复 值可以重复

#get返回键对应的值 pop删除键并返回值 popitem删除并返回键值对item clear清空 update插入 修改
#获取视图 键 值 键值对
print(my_dict.keys()) #键的视图
print(my_dict.values())#值的视图
print(my_dict.items())# 键值对

#视图转列表 list
print(list(my_dict.keys()))
print(list(my_dict.values())[2])  #可以索引index

#get pop
print(my_dict.pop("name","cuowu")) #删除键值对name 并返回值Test,若没有这个键返回cuowu
print(my_dict.popitem()) #删除最后一个键值对 并返回键值对

#update插入
my_dict.update({"sex":"boy","Sex":"girl"})
print(my_dict)
#修改
my_dict["age"]=25
print(my_dict)
#清空clear
my_dict.clear()
print(my_dict)

集合

并集|、交集&、差集-、add、remove、discard、pop

#集合
my_set1={1,7,2,5,0,3,7,8} #会自动去除重复元素
my_set2={1,4,8,9,2,6,7,4,5}
print(my_set1)
# |并集 &交集 -差集 add加入元素 remove/discard删除元素 pop随机删除
print(my_set2 | my_set1) #并集
print(my_set1 & my_set2) #交集
print(my_set1 - my_set2) #差集 在set1但不在set2的元素
#增删
my_set1.add(10) #加入元素10 若存在10则不影响
#my_set1.remove(20) #删除元素20 若不存在20则报错
my_set1.discard(20) #删除元素20且不会报错
print(my_set1)
my_set1.pop()#随机删除一个元素
print(my_set1)

文件操作

打开文件/关闭文件

open/close:open最好指定编码方式,涉及参数:w覆盖写入、a追加写入、r只读、r+、w+、a+

#打开文件 关闭文件
file_path="test.txt"
file1=open(file_path,"a",encoding="utf-8")#追加写入
file1.write("\n第二行")
file1.close() #关闭文件

读取文件

read、readline、readlines

file_path="test.txt"
#读取文件内容 全部读取  按行读取
with open(file_path,"r",encoding="utf-8") as f: #使用with上下文管理器
    content=f.read()
    print(content)


#按行读取 readline每次调用自动读取下一行
with open(file_path,"r",encoding="utf-8") as f:
    line=f.readline()
    while line:
        print(line,end=" ")#空格结尾
        line=f.readline()

#读取内容存到列表中
with open(file_path,"r",encoding="utf-8") as f:
    lines=f.readlines()
    for line in lines:#最后两行的缩进无所谓了
        print(line,end=" ")

写入内容

write、writelines

#写入write
with open(file_path,"w",encoding="utf-8") as f:#w覆盖写入 打开文件时会清空原有内容
    f.write("第一行\n")
    f.write("第二行\n")
    f.write("第三行\n") #且要注意w只写 不可以读

#写入列表writelines
lines=["This is a test\n","第二行\n","第三行\n"]
with open(file_path,"w",encoding="utf-8") as f:#仍然是覆盖写入
    f.writelines(lines)

#追加写入a
with open(file_path,"a",encoding="utf-8") as f:
    f.write("这是第四行\n")

三种打开方式(w+,r+,a+)

r+:以读写模式打开文件,打开时指针在文件的开头,调用write时,数据从指针的当前位置开始写入,会覆盖原有的内容,如果新写入的内容长度小于文件原有内容,未被覆盖的部分则被保留。

这里要注意,在文件开始写入时,写入的字符数(字节)决定了后面的文件内容会留下多少。

比如test.txt文件的内容如下:

执行下述代码:

with open(file_path,"r+",encoding="utf-8") as f:
    #content=f.read()#在读一遍内容之后,指针位于文件末尾,这里屏蔽这行代码
    #print(content) #输出原有的内容
    f.write("11111111111111\n")# 指针在文件开头
    f.write("6bytes\n")
    print(f.read()+"\n")# 这里没有移动指针,所以指针的位置在后续留下内容的开头
    f.seek(0)#移动指针到开头
    print(f.read())#读取所有内容

解析一下代码:第一次write写入的有14个字符,与原有内容第一行This is a test的字符数相同,写入后会覆盖第一行的内容,第二行同样。此时指针的位置就位于新写入的内容的末尾,指针后就是未被覆盖的旧内容,当第一次print时,指针在这个位置开始往后读取文件内容,所以第一次的输出不会包含新写入的前两行;将指针移动到文件的开头后再次print,会输出文件的所有内容。如图:

如果在打开文件后就读取文件内容的话,那么指针就会移动到文件的末尾,此时write的话,那就是追加写入文件了。执行下面代码解释这种情况:

with open(file_path,"r+",encoding="utf-8") as f:
    content=f.read()#在读内容之后,指针位于文件末尾
    print(content) #输出原有的内容
    f.write("追加内容\n")# 指针在新写入内容末尾
    print(f.read())#此时读的话读不出来内容,因为指针已经在末尾了
    f.seek(0)#将指针移到文件开头
    print(f.read())#读取整个文件内容

执行结果如下:

所以关键是要时刻关注指针的变化,且要通过seek()来移动指针,如果指针的位置不正确,读取内容、写入内容都可能会出错。

移动到文件末尾seek(0,2),当然也可以通过read()读一遍文件,指针也会到末尾。

w+:在打开文件时会清空文件内容(可读可写),不要求文件存在(不存在则创建新文件),用如下代码来演示:

lines=["this is a test\n","ok\n","wo\n","shi\n"]
with open(file_path,"w+",encoding="utf-8") as f:
    print(f.read()) #为空,因为打开文件时就已经清空文件内容了
    f.writelines(lines) #写入之后指针在末尾
    f.seek(0)#移动到开头
    print(f.read())#输出

此时会清空原来的内容,写入后指针位于文件末尾,将指针移动到文件开头后再次读取文件内容。

a+:打开文件时不会清空原有的内容,指针在文件的末尾,读取的话先通过seek(0)将指针移动到文件开头再读,注意:即使移动指针到别处,再写入内容时会将指针强制移动到文件末尾写入,所以并不能覆盖写入。

用如下代码来解释:

with open(file_path,"a+",encoding="utf-8") as f:
    print(f.read()) #为空 因为指针在文件的末尾
    f.seek(0)
    print(f.read())#读取文件内容
    #此时指针位于开头
    f.write("追加写入内容\n")#虽然在开头,但写入强制移动到末尾
    f.seek(0)#这一步是为了读取内容
    print(f.read())

结果如图所示:

a+和r+很相似,但是r+的指针是在开头,a+的指针是在末尾,两种打开方式都不会清空原有的内容,只不过r+要求文件必须存在,而a+不需要;另外两者都可以通过write写入内容,只不过a+写入时会强制在文件末尾写入。

python操作json文件

json文件介绍

json是一种轻量化纯文本文件,文件是一个对象{}或者数组[],其对象格式与python中的字典类型相似,都是键值对的形式,所以在通过python读取json文件的时候可以不通过read读取,也不用通过write写入。json的键必须是字符串,最后一个数据不加逗号,且空和布尔值与python略有差别,用如下代码来演示json的格式。

对象形式:

{
  "name": "xiaohong",
  "age": 18,
  "sex": "girl",
  "hobby": ["misic","study","shopping"],
  "address": {
    "country": "China",
    "city": "beijing"
  },
  "school": null,
  "issick": false
}

数组形式:

[
  [99,99,198],
  [-99,-99,-198],
  [0,0,0],
  [50,100,"超出范围"],
  [50,-100,"超出范围"],
  [50,"test1","非数字"],
  [100,20,"超出范围"],
  [-100,20,"超出范围"],
  ["test2",20,"非数字"]

]

json中对象、数组可以相互嵌套,可以复制代码来验证一下输出内容。

读取

在使用方法前,通过import json 导入json库,读取的方法json.load(文件),用如下代码来进行具体的使用方法,注释详细解释了每条语句。

#目前data.json文件已经写好了数据
import json

#打开文件的方式与前面是一样的
with open("data.json","r",encoding="utf-8") as f:
    data:dict=json.load(f) #通过load()读取数据到data
    print(type(data)) #验证一下data的类型,结果是字典dict

#data是字典,就可以用字典的一些方法来进行读取json中的内容了

    print(data.keys()) #获取字典的键
    print(type(data.keys())) #这里的类型是视图,通过list方法转为列表
    print(type(list(data.keys()))) #验证类型是否是list

    #通过索引来获取值
    print(data.get("name")) #获取键name的值
    print(data.get("hobby")) #这里的值是列表

    #多次索引
    print(data.get("hobby")[0])#得到列表的第一个值
    #data.get("address")这里得到的值就是个字典了,可以通过字典的方法继续嵌套
    print(data.get("address").get("city"))

通过json.load()方法后,将json文件内容转化为了字典格式存储在data中,此时就可以通过字典的一些方法来读取操作文件中的内容。

写入

导入json库后,通过json.dump('dict','f')来写入内容,第一个参数是字典,第二个参数是要写入内容的文件。用如下代码来演示。

#先写一个字典
info={
    "name":"xiaoming",
    "age":16,
    "school":"beijingdaxue",
    "hobby":["sleep","eat"],
    "address":{
        "country":"china",
        "city":"shanghai"
    }
}

#w/w+方法打开文件,文件不存在则在同级目录下创建
with open("info.json","w+",encoding="utf-8") as f: #打开info.json文件,不要求该文件存在
    json.dump(info,f,ensure_ascii=False,indent=2)#缩进2
    f.seek(0) #通过之前的内容我们知道,写入后指针在末尾,移动到开头再读取
    print(f.read())

写入后,info.json内容如下图:

这里要注意几个点:由于json文件是一个数组或是一个对象(以对象为例,意思就是一个json文件只能有一个{},所有的键值都在{}中写入),所以只能通过覆盖写入的方式来写入内容到json文件,这里不仅可以用w/w+的方式打开,也可通过a+的方式打开,但是虽然会将内容写入到json文件,在多次执行后会出现多个{},从而不符合json文件的格式要求。利用之前学习到的指针相关内容,可以通过移动指针的方式来读取写入后的json文件内容。

pytest

安装pytest库:

pip install pytest

pip install pytest-html

pytest的作用:编写用例->执行用例->生成测试报告。

分目录管理

pytest.ini:配置文件,根目录

config.py:环境配置,根目录

tests包:存放写的测试用例

common包:读取测试文件数据,比如json文件

data:存放测试数据,比如json ,csv文件等

report:存放测试报告

文件的结构如下图:

配置文件的相关内容,在终端中输入pytest -h可以查看。配置文件pytest.ini的内容如下:

[pytest]
#执行参数
addopts = -s   --html=report/testreport.html  --self-contained-html
#文件路径
testpaths = tests
#python文件名
python_files = test*02.py
#类名
python_classes = Test*03
#方法名
python_functions = test*

配置文件的第一行必须是[pytest],说明是pytest的配置文件;对于pytest文件,每个类的每个方法就是一个测试用例,所以要执行这个测试用例,就要知道这个测试用例的位置在哪,如何执行(执行参数),这就是配置文件的作用,通过定位文件->类名->方法名,就可以找到这个测试用例。

测试用例编写执行

根目录下daima.py的内容,一个加法函数,后续会使用。

def add(a, b):
    return a+b

测试用例写到tests目录下。比如以下的测试用例:

import pytest
from daima import add

class Test_01():
    def test_01(self):
        if add(4,5)== 9: print("测试通过")
        else:print("测试失败")
#有三种执行方式

三种执行方式:终端中输入pytest 文件名;main中执行;配置文件执行

pytest 参数 文件名 执行。

配置文件执行:目前配置文件的内容不动。直接在终端输入pytest。

断言

assert 判断语句 "判断为假时输出的内容"

判断语句一般为:实际结果==预期结果;实际结果 in 预期结果。

若断言未通过,则会给出未通过的用例。如下图:

参数化

观察测试add方法的三个测试用例,会发现除了三个参数不同外,其余都相同,所以我可以在写测试用例时用参数代替,执行时调入实参,方法如下:

#参数化
#每个用例有三个参数,有三个用例,用列表来保存
list1=[[1,2,3],[10,2,13],[15,3,18] ]
class Test_03():
    @pytest.mark.parametrize('a,b,result',list1)
    def test_add(self,a,b,result):
        assert add(a,b) == result

要注意装饰器必须写在方法的上面。此时在终端输入pytest就可以执行(记得更改配置文件)。

生成测试报告

要安装库:pip install pytest-html

在配置文件中设置addopts:

--html=路径/文件名  --self-contained-html,如下代码:

[pytest]
#执行参数
addopts = -s   --html=report/testreport.html  --self-contained-html
#文件路径
testpaths = tests
#python文件名
python_files = test*02.py
#类名
python_classes = Test*03
#方法名
python functions = test*

在pytest执行后,会自动生成测试报告至路径下的文件,若文件不存在则会自动创建文件,测试文件(html)可以通过浏览器打开,如下图:

前后置

方法的前后置,在每次执行一个方法(测试用例)时都会调用,类的前后置,再每次执行一个类时都会调用。比如我这个类有三个测试用例,会先调用setup_class(),然后调用setup_method()、teardowm_method()三次,最后调用teardown_class()。

fixture(夹具)

allure生成测试报告

YAML管理数据

pytest实例

用一个例子(涉及学过的python和pytest)复习相关内容。

两位数的加法计算器,要求如下:

1.1.加数和被加数必须在大于等于-99且小于等于99,否则提示"error,数据的范围小于等于-99/数据的范围大于等于99"
2.如果加数和被加数为非数字,提示 "error,非数字"。

相关代码如下,放在根目录下的文件daima.py中。

def Adder(a, b):
    if not(isinstance(a,(int,float)) and isinstance(b,(int,float))):
        return ('error,非数字')
    if -99<a<99 and -99<b<99: return a+b
    else:
        return 'error,数据的范围必须大于等于-99且小于等于99'

接下来根据要求设计测试用例,并按照(加数,被加数,预期结果)将数据保存到data.json中:

[
  [99,99,198],
  [-99,-99,-198],
  [0,0,0],
  [50,100,"error,数据的范围必须大于等于-99且小于等于99"],
  [50,-100,"error,数据的范围必须大于等于-99且小于等于99"],
  [50,"test1","error,非数字"],
  [100,20,"error,数据的范围必须大于等于-99且小于等于99"],
  [-100,20,"error,数据的范围必须大于等于-99且小于等于99"],
  ["test2",20,"error,非数字"]
]

接下来要读取json数据,并执行测试用例,首先再config.py中定义当前根目录的路径BASE_DIR:

import os

BASE_DIR = os.path.dirname(__file__) #根目录的路径

接下来在common中readjson.py中读取json数据,如下:

#读取json文件
import json

from config import BASE_DIR
def readjson():
    with open(BASE_DIR+"/datas/data.json","r",encoding="utf-8") as f:
        datas=json.load(f) #得到的是列表
        return datas

得到数据后,在测试用例代码中就可以调用这个包来得到datas列表了:

import pytest
from daima import Adder
from common.readjson import readjson
datas=readjson()

class TestAdder():
    @pytest.mark.parametrize('a,b,result',datas)
    def test_add(self,a,b,result):
        assert result == Adder(a,b)

登录接口实例

文件里面提供了一个登录接口的实例,格式严格按照pytest的目录管理要求。

后续会更新requests+pytest做接口测试的内容。

更多推荐