连载|想用Python作自动化测试?Python的输入与输出


构建测试知识体系,欢迎关注javascript

 在写程序过程当中,与文件打交道的是不少,本文就来系统介绍一下python中输入输出相关的知识点,包括介绍文件读写、文件路径处理、序列化与反序列化,经常使用的特殊格式文件的处理等
php

正文字数5243css

输入输出的设备主要有键盘、屏幕、网卡、文件等。java


7.1 键盘和屏幕

  • inputpython

    • 接收键盘的输入,获得的是字符串,name = input('your name:'),要想的到数字,须要进行类型转换nginx

  • printsql

    • 输出到屏幕,print("Hello world")shell

7.2 文件路径

对文件系统的访问,大多经过Python的os模块实现。该模块是Python访问文件系统功能的主要接口。json

经过下面代码能够查看os模块的所有功能。swift

>>> import os>>> dir(os)

例如,获取当前工做目录:

import osprint(os.getcwd())

设置和读取环境变量:

import os
os.environ['WORKON_HOME'] = "value" # 设置环境变量print(os.getenv('WORKON_HOME')) # 读取环境变量

建立目录,或者多层目录

import os
path = "/Users/chunming.liu/learn"if not os.path.exists(path): os.mkdir(path, 777) # 若是是建立多层目录,则使用os.mkdirselse: print("Already exists")

文件路径的操做是工程中常常遇到的。对文件路径的操做是经过os.path模块实现的。能够经过help(os.path)查看全部方法的使用方法。

>>> path = '/Users/chunming.liu/learn/learn_python_by_coding/learn_string/learn_str.py'>>> os.path.basename(path) # 获取文件名'learn_str.py'>>> os.path.dirname(path) # 获取目录名'/Users/chunming.liu/learn/learn_python_by_coding/learn_string'>>> os.path.split(path) # 获取目录名和文件名('/Users/chunming.liu/learn/learn_python_by_coding/learn_string', 'learn_str.py')>>> os.path.splitext(path) #获取扩展名('/Users/chunming.liu/learn/learn_python_by_coding/learn_string/learn_str', '.py')>>> os.path.join("/Users/chunming.liu","learn/learn_python_by_coding/","learn_str.py") # 拼接'/Users/chunming.liu/learn/learn_python_by_coding/learn_str.py'

一般实际工程中,会遇到求当前py文件所在的路径,这时path是__file__

import osprint(os.path.realpath(__file__)) # 若是是符号连接,则返回符号连接指向的文件绝对路径print(os.path.abspath(__file__)) # 文件绝对路径print(os.path.basename(os.path.abspath(__file__))) #当前py文件名print(os.path.dirname(os.path.dirname(__file__))) # 当前文件名的上上级目录

案例应用,查找某个目录下特定扩展名的全部文件名,返回列表。

import os

def find_ext(work_dir, extension): lst = [] for f in os.listdir(work_dir): ext = os.path.splitext(f)[1] if ext == extension: lst.append(os.path.basename(f)) return lst

if __name__ == '__main__': print(find_ext(os.path.dirname(__file__), '.txt'))

7.3 文件读写

open函数用于打开一个文件。

7.3.1 读取整个文件

import os

def read_file(filename): content = None if os.path.exists(filename): # 判断文件存在 with open(filename) as f: content = f.read() # 一次读取整个文件 return content

if __name__ == '__main__': print(read_file(__file__)) # 读取当前py文件内容^.^

7.3.2 一行一行读取

read 函数一次读取整个文件,readlines 函数按行一次读取整个文件。读入文件小时,使用它们没有问题。

可是,若是读入文件大,read 或 readlines 一次读取整个文件,内存就会面临重大挑战。

使用 readline 一次读取文件一行,读入一行处理一行,能解决大文件读取内存溢出问题。

假设文件 python.txt 内容以下,请你统计单词出现频次,按照频次从大到小降序。

思路及步骤:

  1. 每次读入一行。

  2. 选择正则 split 分词,注意观察 a.txt,单词间有的一个空格,有的多个。这些状况,实际工做中确实也会遇到。

  3. 使用 defaultdict 统计单词出现频次。

  4. 按照频次从大到小降序。

import refrom collections import defaultdict
word_count = defaultdict(int) # 声明一个int值型的默认字典with open('python.txt') as f: for line in f: # 哇哈,f能够迭代,一次取一行处理 clean_line = line.strip() if clean_line: sp = re.compile(r'\s+') # 多个空格 words = sp.split(clean_line) # 正则分词 for w in words: word_count[w] += 1 # 默认字典的好处
sorted(word_count.items(), key=lambda x: x[1], reverse=True) # 排序,哇哦print(word_count) # defaultdict(<class 'int'>, {'Python': 3, '3.8.3': 1, 'documentation': 2, 'Welcome!': 1, ......print(word_count['Python']) # 输出3

7.3.3 写入文件

代码思路:

  • 路径不存在,建立路径

  • 写文件

  • 读取同一文件

  • 验证写入到文件的内容是否正确

import os

def write_to_file(file_path, file_name): if not os.path.exists(file_path): os.mkdir(file_path)
whole_path_filename = os.path.join(file_path, file_name) to_write_content = ''' 什么是 Java? Java 是一项用于开发应用程序的技术,可让 Web 变得更有意思和更实用。 Java 与 javascript 并不相同,后者是一种用于建立 Web 页的简单技术,只能在浏览器中运行。 ''' with open(whole_path_filename, mode="w", encoding='utf-8') as f: # 以utf-8编码方式写入文件 f.write(to_write_content)
with open(whole_path_filename, encoding='utf-8') as f: content = f.read() print(content)

if __name__ == '__main__': write_to_file(os.path.dirname(os.path.abspath(__file__)), "java.txt") # 写入到当前py文件所在目录的java.txt中

7.3.4 文件的打开模式

open函数第一个参数是一个文件名称,可使绝对路径也能够是相对当前py文件的相对路径。第二个参数是打开文件的模式,r表示以只读方式打开文件,w表示以覆盖写的方式打开文件,每次写操做都会覆盖以前的内容,x表示文件不存在的时候,先建立再写入。更多的模式能够经过经过help(open)能够查看帮助文档,不少模式能够组合使用。

========= =============================================================== Character Meaning --------- --------------------------------------------------------------- 'r' open for reading (default) 'w' open for writing, truncating the file first 'x' create a new file and open it for writing 'a' open for writing, appending to the end of the file if it exists 'b' binary mode 't' text mode (default) '+' open a disk file for updating (reading and writing) 'U' universal newline mode (deprecated) ========= ===============================================================

7.3.5 with妙用

open() 函数对应 close() 函数,用了with,就不须要手动调用close()函数了,Python会自动帮咱们close文件。

7.4 序列化与反序列化

写程序咱们常常是处理字典、列表、集合以及各类对象,这些对象都是在内存中的。若是咱们想将处理完成后的对象保存到一个文件中或者是传输到网络中?该怎么办呢?这就要用到序列化。

咱们知道,在文件中或者网络中,数据都是一个个字节序列,因此必须把对象转成字节序列,接着就能够写到文件或者传输到网络中了,将对象转成字节序列的过程就是序列化。反之,从文件读取字节序列或者从网络接收字节序列,将其转成对象的过程就是反序列化。

进行序列化的主要目的是:

  1. 持久化保存数据;

  2. 跨平台跨系统的数据交互;

7.4.1 pickle模块

pickle模块是python专用的持久化模块,能够持久化包括自定义类在内的各类数据;只能在python程序之间进行数据交换

看下pickle的官方文档,在Pycharm中,按两下shift,输入pickle则可跳转到源码文件。源码文件的开头有这样一段说明:

"""Create portable serialized representations of Python objects.
See module copyreg for a mechanism for registering custom picklers.See module pickletools source for extensive comments.
Classes:
Pickler Unpickler
Functions:
dump(object, file) dumps(object) -> string load(file) -> object loads(string) -> object

可见pickle模块提供了四个主要的函数:

  • dump——对象序列化到文件对象,就是存入文件;

  • dumps——将对象序列化成string;

  • load——对象反序列化,从文件读取数据;

  • loads——从bytes对象反序列化;

怎么记忆到底哪一个函数时序列化,哪一个函数是反序列化呢?一个办法是,站在内存的角度想,从内存出去就是序列化,进入到内存中就是反序列化。下面看一个使用pickle进行序列化和反序列化的用法:

import pickle

class Person: def __init__(self, name, age): self.name = name self.age = age
def __str__(self): return "Name is {}, age is {}".format(self.name, self.age)
__repr__ = __str__

if __name__ == '__main__': p = Person("Mike", 19) with open("person.txt", 'wb') as wf: # 序列化,以wb模式打开文件 pickle.dump(p, wf)
with open("person.txt", 'rb') as wf: # 反序列化,以rb模式打开文件 p_obj = pickle.load(wf) print("反序列化回来", p_obj)

7.4.2 Json模块

最经常使用的Json序列化,Json是一种轻量级的数据交换格式,跨平台语言,只能对python的基本数据类型进行序列化和反序列化。可是基本能知足大部分应用场景了。

在Pycharm中,按两下shift,输入json则可跳转到源码文件。源码文件中有不少例子,告诉咱们如何使用。

  • 序列化:Python字典对象转成json字符串,json.dumps

import jsonparams = { 'symbol': '123456', 'type': 'limit', 'price': 123.4, 'amount': 23}params_str = json.dumps(params)
  • 反序列化:json字符串转成Python字典,json.loads

original_params = json.loads(params_str)
  • 序列化:将Python字典对象写入到文件中,json.dump

import jsonparams = { 'symbol': '123456', 'type': 'limit', 'price': 123.4, 'amount': 23}with open('params.json', 'w') as fout:  params_str = json.dump(params, fout)
  • 从json文件中读取字符串,转成Python字典,json.load

with open('params.json', 'r') as fin:  original_params = json.load(fin)

7.5 典型文件读写

7.5.1 ini文件

pytest.ini是Pytest提供的配置文件,用来设定pytest在运行中的行为。个人自动化测试项目中的配置以下:

[pytest]disable_test_id_escaping_and_forfeit_all_rights_to_community_support = Trueaddopts = -rsxX -v --alluredir ./allure-resultsmarkers = slow: marks tests as slow (deselect with '-m "not slow"')console_output_style = count
log_file_level = DEBUGlog_file = test_result.loglog_file_date_format = %Y-%m-%d %H:%M:%Slog_file_format = %(asctime)s [%(filename)s:%(lineno)d] %(levelname)s: %(message)s
log_cli = 1log_cli_level = DEBUGlog_cli_date_format=%Y-%m-%d %H:%M:%Slog_cli_format = %(asctime)s [%(filename)s:%(lineno)d] %(levelname)s: %(message)s
  • addopts用来给pytest命令自动添加命令行的选项和参数。--rsxX表示pytest报告全部测试用例被跳过、预计失败、预计失败但实际经过的缘由。这几个字母表示的含义能够经过$ pytest --help查看reporting部分。· --alluredir是我安装的allure-pytest插件提供的参数。

  • testpaths用来告诉pytest在哪里寻找测试用例。

  • markers表示注册的标记,这个marker用来对测试函数进行标记,好比@pytest.mark.smoke。若是addopts中设置了--strict选项,则必须在这里注册的marker才能在测试函数中使用。

  • console_output_style 用来在测试执行过程当中,console上显示执行进度的方式, count表示显示执行到第几个,percentage表示执行的百分比。

  • log开头的那些用来表示日志的配置。上面对日志文件和日志的console输出进行了配置。设置了日志配置,就能够在测试函数中经过下面的方法记录日志了:

import logggingdef test_the_unknown(self): logging.info("跳过不执行,由于被测逻辑尚未被实现") assert 0

使用pytest --help指令能够查看pytest.ini的更多的选项。

讲了这么多,应该知道如何配置pytest.ini来控制pytest的执行过程了。如今回归到本节的主题,如何对ini文件进行读写。Python内置了configparser模块来支持ini文件的读写。使用起来很是简单,先来看下读取ini的方法:

import configparserconfig = configparser.ConfigParser()config.read('../pytest.ini')print(config)section_list = config.sections()print(section_list)print(config['pytest']['addopts'])

再来看一下写ini的方法,很简单,就是给section赋值1个字典:

import osimport configparser
config = configparser.RawConfigParser() # 由于pytest.ini文件中有%,全部这里要用RawConfigParser() 类file = 'pytest.ini'
config['pytest'] = {'disable_test_id_escaping_and_forfeit_all_rights_to_community_support': True, 'addopts': '-rsxX -l --tb=short --strict -v --alluredir ./allure-results', 'markers': "\nslow: marks tests as slow (deselect with '-m \"not slow\"')", 'console_output_style': 'count', 'log_file_level': 'DEBUG', 'log_file': 'test_result.log', 'log_file_date_format': '%Y-%m-%d %H:%M:%S', 'log_file_format': '%(asctime)s [%(filename)s:%(lineno)d] %(levelname)s: %(message)s', 'log_cli': 1, 'log_cli_level': 'DEBUG', 'log_cli_date_format': "%Y-%m-%d %H:%M:%S", 'log_cli_format': '%(asctime)s [%(filename)s:%(lineno)d] %(levelname)s: %(message)s' }
with open(file, 'a') as configfile: config.write(configfile)

7.5.2 yaml文件

YAML文件是软件领域经常使用的配置文件,YAML文件后缀为 .yml。使用空格缩进表示层级关系,空格多少不要紧,只要同一层级左侧对齐便可。对大小写敏感,相比ini文件,我以为更加友好的一点是,能够用#添加注释。

YAML文件中能够存放列表、字符串、数值、对象等数据类型。目前火热的容器集群管理系统Kubernetes中,各类API的配置文件都是使用YAML格式,下面以Kubernetes中Pod对象的YAML的为例,介绍一下YAML文件的格式。

apiVersion: v1 #必选,版本号,例如v1kind: Pod # 必选,代表这个yaml是个Pod对象metadata: name: nginx # 声明这个pod的名字是nginxspec: #必选,Pod中容器的详细定义 containers: #必选,Pod中容器列表 - name: nginx #必选,容器1的名称, -开头表示列表元素 image: nginx #必选,容器的镜像名称 - name: shell #必选,容器2的名称 image: busybox stdin: true tty: true

这个 YAML 文件定义了两个容器:一个是 nginx 容器,一个是开启了 tty 和 stdin 的 shell 容器。

Python中处理YAML文件须要安装PyYaml包。读取上面的YAML文件,方法很简单,经过yaml.load()将YAML文件内容转成一个字典。

import yaml
with open("pod.yml") as yml: pod = yaml.load(yml) # pod是一个字典 print(pod) print(type(pod)) # <class 'dict'>

上面的YAML文件转成字典是这样的:

{ 'apiVersion': 'v1', 'kind': 'Pod', 'metadata': { 'name': 'nginx' }, 'spec': { 'containers': [ { 'name': 'nginx', 'image': 'nginx' }, { 'name': 'shell', 'image': 'busybox', 'stdin': True, 'tty': True }] }}

接下来处理这个字典就很方便了。在自动化测试中,测试数据用YAML格式文件来存储既方便阅读,又方便使用。例如在接口自动化测试中,HTTP的请求对象和预期结果能够放到YAML文件中:

---tests:- case: 验证响应中startcount与请求中的参数一致 # 这是第一条case http: # 这是请求对象,包括请求方法method、请求头headers、请求参数params method: GET path: /v2/movie/in_theaters headers: User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36 params: apikey: 0df993c66c0c636e29ecbb5344252a4a start: 0 count: 10 expected: # 这是预期结果,response表示接口的响应 response: title: 正在上映的电影-上海 count: 10 start: 0- case: 验证响应中title"正在上映的电影-北京" # 这是第二条case http: method: GET path: /v2/movie/in_theaters headers: User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36 params: apikey: 0df993c66c0c636e29ecbb5344252a4a start: 1 count: 5 expected:  response: title: 正在上映的电影-北京 count: 5 start: 1


本文分享自微信公众号 - 明说软件测试(liuchunmingnet)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索