python接口自動化之正則用例參數化的示例詳解

前言

​我們在做接口自動化的時候,處理接口依賴的相關數據時,通常會使用正則表達式來進行提取相關的數據。

​正則表達式,又稱正規表示式、正規表示法、正規表達式、規則表達式、常規表示法(Regular Expression,在代碼中常簡寫為regex、regexp或RE) 。它是一個特殊的字符序列,它能幫助你方便的檢查一個字符串是否與某種模式匹配。在很多文本編輯器裡,正則表達式通常被用來檢索、替換那些匹配某個模式的文本。而Python 自1.5版本起增加瞭re模塊,它提供 Perl 風格的正則表達式模式。

一、正則表達式語法

1.1表示單字符

​單字符:即表示一個單獨的字符,比如匹配數字用\d,匹配非數字用\D。

​除以下語法,也可以匹配指定的具體字符,可以是1個也可以是多個。

字符

功能說明

.

匹配任意1個字符(除瞭\n)

[2a]

匹配[]中括號中列舉的字符,如這裡就是匹配2或者a這兩個字符其中的一個

\d

匹配數字,即0-9

\D

匹配非數字

\s

匹配空白,即空格、tab鍵(tab鍵為兩個空格)

\S

匹配非空白

\w

匹配單詞字符,即a-z、A-Z、0-9、_(數字、字母、下劃線)

\W

匹配非單詞字符

​實例如下,這裡先說明一下findall(匹配規則,要匹配的字符串)這個方法是查找所有匹配的數據,以列表的形式返回,後面會在re模塊進行詳解:

import re
# .:匹配任意1個字符
re1 = r'.'
res1 = re.findall(re1, '\nj8?0\nbth\nihb')
print(res1)	# 運行結果:['j', '8', '?', '0', 'b', 't', 'h', 'i', 'h', 'b']
# []:匹配列舉中的其中一個
re2 = r"[abc]"
res2 = re.findall(re2, '1iugfiSHOIFUOFGIDHFGFD2345a6a78b99cc')
print(res2)	# 運行結果:['a', 'a', 'b', 'c', 'c']
# \d:匹配一個數字
re3 = r"\d"
res3 = re.findall(re3, "dfghjkl32212dfghjk")
print(res3)	# 運行結果:['3', '2', '2', '1', '2']
# \D:匹配一個非數字
re4 = r"\D"
res4 = re.findall(re4, "d212dk?\n$%3;]a")
print(res4)	# 運行結果:['d', 'd', 'k', '?', '\n', '$', '%', ';', ']', 'a']
# \s:匹配一個空白鍵或tab鍵(tab鍵實際就是兩個空白鍵)
re5 = r"\s"
res5 = re.findall(re5,"a s d a  9999")
print(res5)	# 運行結果:[' ', ' ', ' ', ' ', ' ']
# \S: 匹配非空白鍵
re6 = r"\S"
res6 = re.findall(re6, "a s d a  9999")
print(res6)	# 運行結果:['a', 's', 'd', 'a', '9', '9', '9', '9']
# \w:匹配一個單詞字符(數字、字母、下劃線)
re7 = r"\w"
res7 = re.findall(re7, "ce12sd@#a as_#$")
print(res7)	# 運行結果:['c', 'e', '1', '2', 's', 'd', 'a', 'a', 's', '_']
# \W:匹配一個非單詞字符(不是數字、字母、下劃線)
re8 = r"\W"
res8 = re.findall(re8, "ce12sd@#a as_#$")
print(res8)	# 運行結果:['@', '#', ' ', '#', '$']
# 匹配指定字符
re9 = r"python"
res9 = re.findall(re9, "cepy1thon12spython123@@python")
print(res9)	# 運行結果:['python', 'python']

1.2表示數量

​如果要匹配某個字符多次,就可以在字符後面加上數量進行表示,具體規則如下:

字符

功能說明

*

匹配前一個字符出現0次或者無限次,即可有可無

+

匹配前一個字符出現1次或無限次,即至少1次

?

匹配前一個字符出現0次或1次,即要麼沒有,要麼隻有1次

{m}

匹配前一個字符出現m次

{m,}

匹配前一個字符至少出現m次

{m,n}

匹配前一個字符出現從m到n次

​實例如下:

import re
# *:表示前一個字符出現0次以上(包括0次)
re21 = r"\d*"   # 這裡匹配的規則,前一個字符是數字
res21 = re.findall(re21, "343aa1112df345g1h6699")  # 如匹配到a時,屬於符合0次,但因為沒有值所以會為空
print(res21)	# 運行結果:['343', '', '', '1112', '', '', '345', '', '1', '', '6699', '']
 
# ? : 表示0次或者一次
re22 = r"\d?"
res22 = re.findall(re22, "3@43*a111")
print(res22)	# 運行結果:['3', '', '4', '3', '', '', '1', '1', '1', '']
 
# {m}:表示匹配一個字符m次
re23 = r"1[3456789]\d{9}" # 手機號:第1位為1,第2位匹配列舉的其中1個數字,第3位開始是數字,且匹配9次
res23 = re.findall(re23,"sas13566778899fgh256912345678jkghj12788990000aaa113588889999")
print(res23)	# 運行結果:['13566778899', '13588889999']
 
# {m,}:表示匹配一個字符至少m次
re24 = r"\d{7,}"
res24 = re.findall(re24, "sas12356fgh1234567jkghj12788990000aaa113588889999")
print(res24)	# 運行結果:['1234567', '12788990000', '113588889999']
 
# {m,n}:表示匹配一個字符出現m次到n次
re25 = r"\d{3,5}"
res25 = re.findall(re25, "aaaaa123456ghj333yyy77iii88jj909768876")
print(res25)	# 運行結果:['12345', '333', '90976', '8876']

1.2.1匹配分組

字符

功能說明

|

匹配左右任意一個表達式

(ab)

將括號中字符作為一個分組

實例如下:

import re
# 同時定義多個規則,隻要滿足其中一個
re31 = r"13566778899|13534563456|14788990000"
res31 = re.findall(re31, "sas13566778899fgh13534563456jkghj14788990000")
print(res31)	# 運行結果:['13566778899', '13534563456', '14788990000']
# ():匹配分組:在匹配規則的數據中提取括號裡的數據
re32 = r"aa(\d{3})bb"	# 如何數據符合規則,結果隻會取括號中的數據,即\d{3}
res32 = re.findall(re32, "ggghjkaa123bbhhaa672bbjhjjaa@45bb")
print(res32)	# 運行結果:['123', '672']

1.3 表示邊界

字符

功能說明

^

匹配字符串開頭,隻能匹配開頭

$

匹配字符串結尾,隻能匹配結尾

\b

匹配一個單詞的邊界(單詞:字母、數字、下劃線)

\B

匹配非單詞的邊界

​實例如下:

import re
# ^:匹配字符串的開頭
re41 = r"^python"   # 字符串開頭為python
res41 = re.findall(re41, "python999python")  # 隻會匹配這個字符串的開頭
res411 = re.findall(re41, "1python999python")  # 因為開頭是1,第1位就不符合瞭
print(res41)	# 運行結果:['python']
print(res411)	# 運行結果:[]
 
# $:匹配字符串的結尾
re42=r"python$"	# 字符串以python結尾
res42 = re.findall(re42, "python999python")
print(res42)	# 運行結果:['python']
 
# \b:匹配單詞的邊界,單詞即:字母、數字、下劃線
re43 = r"\bpython"  # 即匹配python,且python的前一位是不是單詞
res43 = re.findall(re43, "1python 999 python")  # 這裡第1個python的前1位是單詞,因此第1個是不符合的
print(res43)	# 運行結果:['python']
 
# \B:匹配非單詞的邊界
re44 = r"\Bpython"  # 即匹配python,且python的前一位是單詞
res44 = re.findall(re44, "1python999python")
print(res44)	# 運行結果:['python', 'python']

二、貪婪模式

​python裡數量詞默認是貪婪的,總是嘗試匹配盡可能多的字符,而非貪婪模式則是嘗試匹配盡可能少的字符,在表示數量的表達式後加上問號(?)就可以關閉貪婪模式。

​ 如下例子,匹配2個以上的數字,如果符合條件它會一直匹配到不符合才停止,如其中的34656fya,34656符合2個數字以上,那麼它會一直匹配到6為止,如果關閉貪婪模式,那麼在滿足2個數字時就會停止,最後可以匹配到34、65。

import re
# 默認的貪婪模式下
test = 'aa123aaaa34656fyaa12a123d'
res = re.findall(r'\d{2,}', test)
print(res)	# 運行結果:['123', '34656', '12', '123']
 
# 關閉貪婪模式
res2 = re.findall(r'\d{2,}?', test)
print(res2)	# 運行結果:['12', '34', '65', '12', '12']

三、re模塊

​在python中使用正則表達式,就會用到re模塊來進行操作,提供的方法一般需要傳入兩個參數:

  • 參數1: 匹配的規則
  • 參數2:要進行匹配的字符串

3.1 re.findall()

​查找所有符合規范的字符串,以列表的形式返回。

import re
test = 'aa123aaaa34656fyaa12a123d'
res = re.findall(r'\d{2,}', test)
print(res)	# 運行結果:['123', '34656', '12', '123']

3.2re.search()

​查找第一個符合規范的字符串,返回的是一個匹配對象,可以通過group()將匹配到的數據直接提取出來。

import re
s = "123abc123aaa123bbb888ccc"
res2 = re.search(r'123', s)
print(res2)  # 運行結果:<re.Match object; span=(0, 3), match='123'>
 
# 通過group將匹配到的數據提取出來,返回類型為str
print(res2.group())   # 運行結果:123

​返回的匹配對象中,span為匹配到的數據的下標范圍,match則是匹配到的值。

​group()參數說明:

  • 不傳參數:獲取的是匹配到的所有內容
  • 傳入數值:可以通過參數來指定,獲取第幾個分組中的內容(獲取第1個分組,傳入參數1,獲取第2個分組,傳入參數2,依次類推。)
import re
s = "123abc123aaa123bbb888ccc"
re4 = r"aaa(\d{3})bbb(\d{3})ccc"	# 這裡分組就是前面說到的匹配語法:()
res4 = re.search(re4, s)
print(res4)
# group不傳參數:獲取的是匹配到的所有內容
# group通過參數指定,獲取第幾個分組中的內容(獲取第1個分組,傳入參數1,獲取第2個分組,傳入參數2,依次類推..
print(res4.group())
print(res4.group(1))
print(res4.group(2))

3.3 re.match()

​從字符串的起始位置進行匹配,匹配成功則返回匹配到的對象,如果開頭的位置不符合匹配的規則,不會繼續往後面去匹配,直接返回None。re.match()與re.search()都是隻匹配一個,不一樣的是,前者隻匹配字符串的開頭,後者則是會匹配整個字符串,但隻獲取第一個符合的數據。

import re
s = "a123abc123aaa1234bbb888ccc"
# match:隻匹配字符串的開頭,開頭不符合就返回None
res1 = re.match(r"a123", s)
res2 = re.match(r"a1234", s)
print(res1)  # 運行結果:<re.Match object; span=(0, 4), match='a123'>
print(res2)  # 運行結果:None

3.4re.sub()

​檢索和替換:用於替換字符串中的匹配項

​re.sub()參數說明:

  • 參數1:待替換的字符串
  • 參數2:目標字符串
  • 參數3:要進行替換操作的字符串
  • 參數4:可以指定最多替換的次數,非必填(默認替換所有符合規范的字符串)
import re
s = "a123abc123aaa123bbb888ccc"
# <font color="#FF0000">參數1:</font>待替換的字符串
# <font color="#FF0000">參數2:</font>目標字符串
# <font color="#FF0000">參數3:</font>要進行替換操作的字符串
# <font color="#FF0000">參數4:</font>可以指定最多替換的次數,非必填(默認替換所有符合規范的字符串)
res5 = re.sub(r'123', "666", s, 4)
print(res5)  # 運行結果:a666abc666aaa666bbb888ccc

四、用例參數化

​在接口自動化測試中,我們的測試數據都是保存在excel中的,有些參數如果寫死一個數據,可能換個場景或者換個環境就不能用瞭,那麼切換環境時就需要先把新環境的測試數據準備好,並且能支持去跑我們的腳本,或者把excel的數據修改為適合新環境的測試數據,維護的成本較高。因此就需要把我們的自動化腳本測試數據盡量地參數化,降低維護成本。

我們先看簡單版的參數化,以登錄為例,登錄時用到的賬號、密碼等信息都可以提取出來放到配置文件,修改數據或更換環境時直接在配置文件中統一修改就可以瞭。

​但如果有多個不同的數據需要參數化呢,每個參數都加個判斷去替換數據嗎?這樣的代碼既囉嗦又不好維護,這時re模塊就可以用上瞭,直接看一個實例:

import re
from common.myconfig import conf
class TestData:
    """用於臨時保存一些要替換的數據"""
    pass
def replace_data(data):
    r = r"#(.+?)#"	# 註意這個分組()內的內容
    # 判斷是否有需要替換的數據
    while re.search(r, data):
        res = re.search(r, data)	# 匹配出第一個要替換的數據
        item = res.group()	# 提取要替換的數據內容
        key = res.group(1)	# 獲取要替換內容中的數據項
        try:
            # 根據替換內容中的數據項去配置文件中找到對應的內容,進行替換
            data = data.replace(item, conf.get_str("test_data", key))
        except:
            # 如果在配置文件中找不到就在臨時保存的數據中找,然後替換
            data = data.replace(item, getattr(TestData, key))
    return data

​註意這裡的正則表達式是有使用?關閉貪婪模式的,因為測試數據中可能會需要參數化2個或以上的數據,如果不關閉貪婪模式,它就隻能匹配搭配一個數據,舉例如下:

import re
data = '{"mobile_phone":"#phone#","pwd":"#pwd#","user":#user#}'
r1 = "#(.+)#"
res1 = re.findall(r1, data)
print(res1)  # 運行結果:['phone#","pwd":"#pwd#","user":#user']	註意這裡單引號隻有一個數據
print(len(res1))      # 運行結果:1
r2 = "#(.+?)#"
res2 = re.findall(r2, data)
print(res2)  # 運行結果:['phone', 'pwd', 'user']
print(len(res2))      # 運行結果:3

​另外提到的一個用於臨時保存數據的類,這裡主要用於保存接口返回的數據,因為有些測試數據是動態變化的,可能要依賴於某個接口,後面的測試用例又需要這些數據,那麼我們在接口返回時就可以保存到這個類裡作為一個類屬性,接著在需要用這個數據的測試用例時,把這個類屬性提取出來替換到測試數據中即可。提示:設置屬性setattr(對象, 屬性名, 屬性值),獲取屬性值getattr(對象, 屬性名)。

總結

到此這篇關於python接口自動化之正則用例參數化的文章就介紹到這瞭,更多相關python正則用例參數化內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: