連載|想用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源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索