How to do interface automation? Interface automated testing - regular use case parameterization (instance)


foreword

When we are doing interface automation, we usually use regular expressions to extract related data when dealing with related data that the interface depends on.

Regular expression, also known as regular expression, regular expression, regular expression, regular expression, regular expression (Regular Expression, often abbreviated as regex, regexp or RE in code).

It is a special character sequence that helps you easily check whether a string matches a certain pattern. In many text editors, regular expressions are often used to retrieve and replace text that matches a certain pattern.

regular expression syntax

Indicates a single character
Single character: means a single character, for example, \d is used to match numbers, and \D is used to match non-digits.
In addition to the following syntax, it can also match specified specific characters, which can be one or more.

character Function Description
. Match any 1 character (except \n)
[2a] Match the characters listed in [] square brackets, for example, it matches one of the two characters 2 or a
\d Match digits, i.e. 0-9
\D match non-digit
\s Match blanks, that is, spaces, tab keys (tab keys are two spaces)
\S match non-blank
\w Match word characters, i.e. az, AZ, 0-9, _ (numbers, letters, underscore)
\W matches non-word characters

The example is as follows, here is a description of findall (matching rules, the string to be matched) This method is to find all the matching data and return it in the form of a list, which will be explained later in the re module:

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']

Indicates the quantity

If you want to match a certain character multiple times, you can add the number after the character to indicate, the specific rules are as follows:

character Function Description
* Match the previous character 0 times or infinite times, it can be optional
+ Match the previous character 1 time or unlimited times, that is, at least 1 time
? Match the previous character 0 or 1 time, that is, either none or only 1 time
Matches the previous character m times
Matches at least m occurrences of the previous character
Match the previous character from m to n times

Examples are as follows:

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']

match group

character Function Description
I Match any left or right expression
(ab) Treat the characters in parentheses as a group

Examples are as follows:

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']

Indicates the boundary

character Function Description
^ Match the beginning of the string, only the beginning
$ Match the end of the string, only the end
\b Matches a word boundary (word: letter, digit, underscore)
\B Match non-word boundaries

Examples are as follows:

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']

greedy mode

Quantifiers in python are greedy by default. They always try to match as many characters as possible. The non-greedy mode tries to match as few characters as possible. Add a question mark (?) to the expression indicating the quantity to turn off the greedy mode .

The following example matches more than 2 numbers. If it meets the conditions, it will match until it does not match. For example, 34656fya, 34656 matches more than 2 numbers, then it will match until 6. If the greedy mode is turned off, then It will stop when it meets 2 numbers, and finally it can match 34 and 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 module

When using regular expressions in python, the re module will be used for operations. The methods provided generally require two parameters to be passed in:

Parameter 1: The matching rule
Parameter 2: The string to be matched

re.findall()

Find all strings that match the specification, and return them as a list.

import re


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

Find the first string that meets the specification, and return a matching object, and the matched data can be directly extracted through 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

In the returned match object, span is the subscript range of the matched data, and match is the matched value.

group() parameter description:
no parameter is passed: all the matched content is obtained

Incoming value: It can be specified by parameters to get the content of the first group (get the first group, pass in parameter 1, get the second group, pass in parameter 2, and so on.)

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))

re.match()

Match from the starting position of the string, and return the matched object if the matching is successful. If the starting position does not meet the matching rules, it will not continue to match and return None directly. Both re.match() and re.search() match only one. The difference is that the former only matches the beginning of the string, while the latter matches the entire string, but only gets the first matching data.

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

re.sub()

Retrieval and replacement: used to replace the matching items in the string
re.sub() Parameter description:
Parameter 1: the string to be replaced
Parameter 2: the target string
Parameter 3: the string to be replaced
Parameter 4: can be specified The maximum number of replacements, not required (by default, all strings that meet the specification are replaced)

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

use case parameterization

In the interface automation test, our test data are all stored in excel. If some parameters are written to death, they may not be available in a different scene or environment. When switching environments, you need to first save the test data in the new environment. It is ready and can support to run our scripts, or modify the excel data to test data suitable for the new environment, and the maintenance cost is relatively high.

Therefore, we need to parameterize our automated script test data as much as possible to reduce maintenance costs.

Let’s first look at the parameterization of the simple version. Taking login as an example, the account number, password and other information used for login can be extracted and placed in the configuration file. When modifying data or changing the environment, it can be directly modified in the configuration file.

C1

C2

But if there are multiple different data that need to be parameterized, should each parameter add a judgment to replace the data? Such code is long-winded and difficult to maintain. At this time, the re module can be used. Let's look at an example directly:

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

Note: The regular expression here is used? Turn off the greedy mode, because the test data may need to parameterize 2 or more data, if the greedy mode is not turned off, it can only match and match one data, for example:

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

In addition, a class for temporarily saving data is mentioned, which is mainly used to save the data returned by the interface, because some test data is dynamically changing and may depend on a certain interface, and later test cases need these data.

Then we can save it in this class as a class attribute when the interface returns, and then extract this class attribute and replace it in the test data when we need a test case that uses this data.

Tip: set the attribute setattr (object, attribute name, attribute value), get the attribute value getattr (object, attribute name).

The following is the most complete software test engineer learning knowledge architecture system diagram in 2023 that I compiled

1. From entry to mastery of Python programming

Please add a picture description

2. Interface automation project actual combat

Please add a picture description

3. Actual Combat of Web Automation Project

Please add a picture description

4. Actual Combat of App Automation Project

Please add a picture description

5. Resume of first-tier manufacturers

Please add a picture description

6. Test and develop DevOps system

Please add a picture description

7. Commonly used automated testing tools

Please add a picture description

Eight, JMeter performance test

Please add a picture description

9. Summary (little surprise at the end)

Embrace the ups and downs, march towards glory, and struggle is the rhythm of realizing dreams. Go forward bravely, not afraid of difficulties, and water the flowers in your heart with sweat. Believe in your own strength, develop your unlimited potential, create your own magnificent life, and let every effort create indelible brilliance!

Reject mediocrity, pursue excellence, and struggle is the fire that inspires the heart. Set sail, forge ahead, and pave the way to success with hard work. Believe in persistence and meet challenges.

Determination is like fire, chasing dreams, and struggle is the driving force for burning persistence. Let go of passion, go beyond the limit, and write the splendid chapter of life with hard work. Believe in miracles, embrace challenges, let every effort become an opportunity to change your destiny, and bloom your own unique brilliance!

Guess you like

Origin blog.csdn.net/x2waiwai/article/details/131981661