Python实现Web UI自动化测试实战:Selenium 3/4+unittest/Pytest+GitLab+Jenkins
上QQ阅读APP看书,第一时间看更新

3.7 文件读写

在自动化测试过程中,要实现测试数据、配置等和测试脚本的分离,往往需要Python来处理各种类型的数据。因此,我们来学习一下相关的知识。

3.7.1 Python中的open函数

在做自动化测试的过程中,常会将一些系统配置、测试数据等保存到文件中。这时候就需要借助Python的open函数来操作文件。

我们先来看一个文件操作的简单示例(示例代码:test3_22.py)。

#通过open打开文件:第一个参数为文件名;第二个参数为模式;第三个参数为编码方式。
f = open(“test3_1.py”,“r”,encoding='utf8')
# 通过read读取文件的全部内容
data = f.read()
print(data) # 输出
f.close() # 关闭文件

对文件的操作步骤简单总结如下。

●打开文件,得到文件句柄并赋给变量。

●借助句柄对文件进行操作。

●关闭文件。

接下来,我们看一下open函数的完整语法格式。

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

参数说明如下。

●file:必选,文件路径(相对或者绝对路径)。

●mode:可选,文件打开模式,默认为“r”。

●buffering:设置缓冲。

●encoding:一般使用UTF-8。

●errors:报错级别。

●newline:区分换行符。

●closefd:传入的file参数类型。

mode的参数有多种取值方式,下面介绍一些常用的参数,如表3-3所示。

表3-3 文件打开模式mode的参数

接下来,我们再来看一下open函数创建的对象都可以使用哪些常见的方法,如表3-4所示。

表3-4 文件常见操作方法

接下来,我们用几个示例脚本来演示表3-4所示的方法如何应用。在Chapter_3目录下面新建一个“1.txt”文件,内容如下。

the first line
the second line
the third line
the forth line
the fifth line

示例一:通过read读取全部内容(示例代码:test3_23.py)。

f = open("1.txt","r",encoding='utf8')
# 通过read读取文件全部内容
data1 = f.read()
print("read对应的结果: {}".format(data1))
f.close() # 关闭文件

示例二:通过readline读取某行内容(示例代码:test3_24.py)。

f = open("1.txt","r",encoding='utf8')
# 通过readline读取一行内容
data2 = f.readline()
print("readline对应的结果: {}".format(data2))
f.close() # 关闭文件

示例三:通过readlines读取全部内容,返回列表(示例代码:test3_25.py)。

f = open("1.txt","r",encoding='utf8')
# 通过readlines读取全部内容,返回列表
data3 = f.readlines()
print("readlines对应的结果: {}".format(data3))
f.close() # 关闭文件

运行结果如下。

readlines对应的结果: ['the first line\n', 'the second line\n', 'the third line\n', 'the forth line\n', 'the fifth line']

示例四:通过write写入文字(示例代码:test3_26.py)。

f = open("2.txt","w",encoding='utf8')
f.write('像Storm一样飞')
f.close()

示例五:通过writelines将一个列表写入文件中。需要注意的是,如果不加换行符,则只会生成一行文字(示例代码:test3_27.py)。

f = open("3.txt","w",encoding='utf8')
data = ['name\n','age\n','sex']
f.writelines(data)
f.close()

注意▶ 我们读取文件是把内容读取到内存中的,如果不关闭它,就会一直占用系统资源,而且还可能导致其他的安全隐患。因此,这里提醒读者在通过open打开文件,操作完数据后,一定要记得使用close方法关闭文件。

为了避免忘记手动关闭文件,这里给大家推荐一种打开文件的新写法:with open。使用该方法,可以不用手动关闭文件(示例代码:test3_28.py)。

with open("1.txt", "r") as f:
     data = f.read()
     print(data)

3.7.2 JSON文件

在自动化测试过程中,我们会将部分数据保存到JSON(JavaScript Object Notation,JavaScript对象表示法)文件中,因此,学习使用Python处理JSON数据显得非常必要。JSON是一种常用的数据交换格式。JSON文件有以下特点。

●JSON是存储和交换文本信息的语法,类似XML。

●JSON比XML更小、更快、更易解析。

●JSON是轻量级的文本数据交换格式。

●JSON独立于语言。JSON使用JavaScript语法来描述数据对象,但是JSON仍然独立于语言和平台。JSON解析器和JSON库支持许多不同的编程语言。目前非常多的动态编程语言(PHP、JSP、.NET等)都支持JSON。

●JSON具有自我描述性,更易理解。

JSON的语法规则如下。

●JSON数据用大括号括起来。

●数据在“名称/值”对中,名称和值用冒号分隔,类似Python中的字典。

●名称必须用双引号引起来,值是否需要双引号引起来要视值的类型而定。

●数据由逗号分隔。

下面来看一个JSON格式的数据示例。

{"name":"storm", "age":30}

上面的JSON数据共有两个“名称/值”对,从格式上来看,和Python中的字典非常类似。实际上,我们在处理JSON数据时,很多情况下也确实将其转成字典来处理。不过需要注意的是,JSON数据必须用双引号引起来,不能使用单引号。

来看一下JSON值类型。

●数字(整数或浮点数),如{ "age":30 }。

●字符串(在双引号中),如{ "name":"Storm" }。

●逻辑值(True或False),如{ "flag":True }。

●数组(在中括号中),如{"sites": ["name","url"]}。

●对象(在大括号中),如{"student":{"name":"dzl"}}。

●null,如{“age”:null }。

JSON文件的扩展名为“.json”。在Chapter_3中新建一个文件,并命名为“test3_29.json”,代码如下。

{"name":"storm", "age":30, "sex": "男"}

Python 3.x版本自带json模块,不需要自己安装。json模块用于字符串和JSON数据类型间的转换,json模块提供了4个功能:dumps、dump、loads、load。

先来看dumps,其作用是将字典转换为字符串。

import json

dict1 = {"name":"storm","age": 35}
print(dict1)
print(type(dict1))

# dumps将字典转换为字符串
j1 = json.dumps(dict1)
print(j1)
print(type(j1))

运行结果如下。

{'name': 'storm', 'age': 35}
<class 'dict'>
{"name": "storm", "age": 35}
<class 'str'>

再来看dump,其作用是将字典转换为字符串,并写入JSON文件中。

import json

dict1 = {"name":"storm","age": 30}
print(dict1)
print(type(dict1))

# 将字典数据写入txt文件
with open("1.txt","w") as f:
     j1 = json.dump(dict1,f)
     print(j1)
     print(type(j1))

运行结果如下。

{'name': 'storm', 'age': 30}
<class 'dict'>
None
<class 'NoneType'>

然后看loads,其作用是将字符串转换为字典。

import json

# str1是单引号引起来的字符串
str1 = '{"name":"storm","age": 30}'
print(str1)
print(type(str1))

# loads将字符串转换成字典
dic = json.loads(str1)
print(dic)
print(type(dic))

运行结果如下。

{"name":"storm","age": 30}
<class 'str'>
{'name': 'storm', 'age': 30}
<class 'dict'>

最后看load,其作用是把文件打开,并把字符串转换为数据类型。

import json

with open("1.txt",'r') as f:
    print(type(f))
    dic = json.load(f)
    print(dic)
    print(type(dic))

运行结果如下。

<class '_io.TextIOWrapper'>
{'name': 'storm', 'age': 30}
<class 'dict'>

接下来,我们借助Python处理JSON文件。

◆读取字典类型的数据文件

在Chapter_3文件目录下创建一个名为“login_account.json”的文件,用来保存某个系统的登录账号信息,内容如下。

{
"user1":{"name":"storm","password":"123456"},
"user2":{"name":"duzl","password":"123123"}
}

然后通过脚本读取该文件的内容。

import json
file = "login_account.json"

with open(file,'r') as f:
     users = json.load(f)
     print(type(f))
     print(type(users))
     print(users)

for user in users:
     name = users[user]['name']
     password = users[user]['password']
     print(name,password)

运行结果如下。

<class '_io.TextIOWrapper'>
<class 'dict'>
{'user1': {'name': 'storm', 'password': '123456'}, 'user2': {'name': 'duzl', 'password': '123123'}}
storm 123456
duzl 123123 

◆读取列表类型的数据文件

同样,在Chapter_3文件目录下创建一个名为“myarray.json”的文件,内容如下。

[
  {
      "name":"storm",
    "age":30
  },
  {
    "name":"lina",
    "age":22
  }
]

然后我们用脚本读取文件中的数据。

import json

file = "myarray.json"

with open(file,'r') as f:
     ss = json.load(f)

for s in ss:
      print(s)
      print(s["name"])
      print(s["age"])

运行结果如下。

{'name': 'storm', 'age': 30}
storm
30
{'name': 'lina', 'age': 22}
lina
22

前面我们说了,在自动化测试过程中,我们经常会读取JSON文件。为了简化操作,我们可以封装一个读取JSON文件的函数,该函数支持解析两层的JSON文件。后续我们就可以直接调用该函数来解析JSON文件了,代码如下。

import json

'''
封装一下解析JSON文件的函数,支持两层的JSON文件,两个键
'''
def parse_json(file,key1,key2):
     mylist = []
     with open(file,'r',encoding='utf8') as f:
          data = json.load(f)

     for i in data:
          mylist.append((data[i][key1],data[i][key2]))
     return mylist
if __name__ == '__main__':
     account_info = parse_json('login_account.json', 'name','password')
     print(account_info)

3.7.3 YAML文件

在Web UI自动化测试中,我们可以将用到的配置信息保存在YAML格式的文件中。YAML是YAML Ain't a Markup Language的缩写。YAML的语法和其他高级语言类似,它用空白符号表示缩进,特别适合用来表达配置文件。该类型文件的扩展名为“.yml”,如storm.yml。

YAML文件的基本语法规则如下。

●大小写敏感。

●使用缩进表示层级关系。

●缩进不允许使用Tab键,只允许使用空格键。

●缩进的空格数不重要,只要相同层级的元素左对齐即可。

●“#”表示注释。

YAML文件支持多种数据类型。

●对象:键-值对的集合,又称为映射(mapping)、哈希(hashes)、字典(dictionary)。

●数组:一组按次序排列的值,又称为序列(sequence)、列表(list)。

接下来,我们通过代码来演示一下如何使用Python处理各种类型的YAML文件。PyYaml是Python中一个专门用于对YAML文件进行操作的模块。该模块同样可以借助pip来安装,安装命令如下。

C:\Users\duzil>pip3 install pyyaml

(1)YAML对象

这里,我们先准备一个名为“my_yaml_1.yml”的文件,内容如下。

url: "http://localhost:81/redmine/"
ip: "127.0.0.1"

这里需要注意,冒号前面没有空格,冒号后面有一个空格。然后编写脚本来读取该文件。

import yaml

with open('my_yaml_1.yml', 'r', encoding='utf8') as f:
     data = yaml.load(f, Loader=yaml.FullLoader)
     print(data)
     print(data['url'])
     print(data['ip'])

这里需要注意以下两点。

●我们通过yaml.load来处理YAML文件的内容。

●注意要加一个默认参数Loader=yaml.FullLoader。YAML 5.1版本后弃用了yaml.load(file)这个用法,但出于安全考虑需要指定Loader,通过默认加载器(FullLoader)可以禁止执行任意函数,这里了解即可。

运行结果如下。

{'url': 'http://localhost:81/redmine/', 'ip': '127.0.0.1'}
http://localhost:81/redmine/
127.0.0.1

(2)YAML数组

同样,我们先准备一个名为“my_yaml_2.yml”的文件,内容如下。

- storm
- sk
- shadow
- queen

注意▶ “-”后面有一个空格。

然后通过脚本来处理该YAML文件。

import yaml

with open('my_yaml_2.yml', 'r', encoding='utf8') as f:
     data = yaml.load(f, Loader=yaml.FullLoader)
     print(data)

运行结果如下。

['storm', 'sk', 'shadow', 'queen']

(3)YAML复合结构

同样,我们先准备一个名为“my_yaml_3.yml”的文件,内容如下。

websites:
  URL: http://localhost/
  IP: 127.0.0.1
  Port: 81

然后通过脚本来读取该YAML文件。

import yaml

with open('my_yaml_3.yml', 'r', encoding='utf8') as f:
     data = yaml.load(f, Loader=yaml.FullLoader)
     print(data)
     print(data['websites']['URL'])
       print(data['websites']['IP'])
       print(data['websites']['Port'])

运行结果如下。

{'websites': {'URL': 'http://localhost/', 'IP': '127.0.0.1', 'Port': 81}}
http://localhost/
127.0.0.1
81

在Web UI自动化测试中,可以将系统用到的配置信息以复合结构保存到一个特定文件中。因此,我们可以封装一个函数来读取YAML文件的信息,文件名为“parse_yml.py”,脚本如下。

import yaml

'''
通过传递文件名、section和key,读取YAML文件中的内容
'''
def parse_yml(file, section, key):
     with open(file, 'r', encoding='utf8') as f:
          data = yaml.load(f, Loader=yaml.FullLoader)
          return data[section][key]

if __name__ == '__main__':
     value = parse_yml('my_yaml_3.yml', 'websites', 'URL')
     print(value)

3.7.4 CSV文件

在自动化测试过程中,可以将相对复杂的测试数据放置到表格中,如放置到Excel或CSV(Comma Separated Values,逗号分隔值,有时也称为字符分隔值,因为分隔字符也可以不是逗号)文件中。

Excel文件大家相对比较熟悉,Python有丰富的第三方库来处理Excel文件,需要注意的是Excel文件扩展名分为“.xls”和“.xlsx”,需要不同的库来处理。不过本书不介绍如何使用Python处理Excel文件,而是介绍一种更简洁、更轻量的文件格式:CSV。该文件以纯文本形式存储表格数据(数字和文本)。

日常工作中,大家经常用Excel或WPS来打开扩展名为“.csv”的文件,实际上完全可以使用写字板来打开它,你会发现它是一种用逗号分隔的数据文件,如图3-3所示。

图3-3 CSV文件

接下来,我们在Chapter_3目录下新建一个CSV文件,并命名为“my_csv_1.csv”,文件内容如下。

name,password,status
admin,error,0
admin,rootroot,1

借助代码来读取文件,步骤如下。

●导入csv模块。

●借助csv.reader来处理数据。

import csv

with open('my_csv_1.csv', 'r', encoding='utf8') as f:
     data = csv.reader(f)
     print(data)
     for i in data:
           print(i)

运行结果如下。

<_csv.reader object at 0x0077E4B0>
['name', 'password', 'status']
['admin', 'error', '0']
['admin', 'rootroot', '1']

在自动化测试中,我们经常需要将CSV文件中的数据返回为一个嵌套列表,这样可以将数据作为测试的参数来使用(后面章节将详细介绍)。因此,我们可以封装一个函数来实现该功能。示例代码:parse_csv.py。

import csv

def parse_csv(file):
     mylist = []
     with open(file, 'r', encoding='utf8') as f
          data = csv.reader(f)
          for i in data:
                mylist.append(i)
          del mylist[0] # 删除标题行的数据
          return mylist

if __name__ == '__main__':
     data = parse_csv('my_csv_1.csv')
     print(data)

本节介绍了如何使用Python读取JSON、YAML、CSV文件中的内容,后续我们会将这些知识应用到自动化测试框架中,希望大家掌握。

注意▶ 文件名中别用居中的横杠,虽然不会报错,但是在文件相互调用的时候会出现失败的情况,建议使用下划线来代替居中的横杠。