利用脚本进行接口测试

Sabthever

当接口写完需要测试时,我们可以在接口文档或者用postman单独调用某个接口来进行测试。
但是,有的时候我们需要多次调用某个接口,或者嵌套调用多个接口。比如接口A调用后返回的一个数组,其中的值作为接口B的某个参数,并且多次调用。
这种时候单独调用一个接口就很难做到了,要利用脚本进行测试。这边,讲解两种嵌套调用接口的方式,一种是postman+javascript的方式,一种是python的方式。单独用javascript的方式可以通过第一种方式类推一下。

问题:接口A返回内容,给接口B循环调用

我先来介绍一下手动执行的方法。

对于获取部门的接口A:http://localhost:8095/unit,需要请求头和请求体如下。

1
2
3
4
5
6
7
8
// 请求头
Authorization:xxx

// 请求体
{
"area": null,
"userId": 702
}

然后获取的数据如下

1
2
3
4
5
6
7
8
9
{
"code": 200,
"msg": "请求成功",
"data": [
"AS-M",
"AD-H",
……
]
}

对于上面获取的数据的data就是我接口B根据部门获取相应数据的其中一个参数combatUnit,而接口B每次只能接收一个,所以要多次调用:http://localhost:8095/areaData。该接口的请求头和请求体如下:

1
2
3
4
5
6
7
8
9
10
11
12
// 请求头
Authorization:xxx

// 请求体
{
"attribute1": "",
"businessTab": "",
"combatUnit": "AS-M",
"region": "",
"role": "4",
"subsidiary": ""
}

这时,B中的参数要一个个放入combatUnit中调用,返回结果如下。对于有些部门调用接口B的时候会出现其中的list为null的情况,我要找出这些部门。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"code": 200,
"msg": "请求成功",
"data": {
"list": [
{
"area": "中东",
"point": "[54.458379258595116, 24.388822399734615]",
"color": "#00B87A"
}
],
"centerPoint": "[81.106187,6.629154]"
}
}

如果一个个调用接口B,几百次调用对于手动来说不仅慢还易错,因此就需要使用脚本来测试。

一. postman+javascript

在postman配置一个接口中有一个Scripts,点进去后有个Post-response我们可以把内容写在这里。当我们对此接口send的时候,在这边的脚本就会执行。

image-20250722161922979

这个脚本中用的是javascripts脚本,在当前调用pm.response可以调取到当前接口的返回值。上面说了接口A返回的值形如

1
2
3
4
5
6
7
8
9
{
"code": 200,
"msg": "请求成功",
"data": [
"AS-M",
"AD-H",
……
]
}

因此可以用pm.response.json().data获取整个数组,当然这个脚本得在接口A的Scripts中编写才能获得到数据。
准备好共用的请求头,然后请求体和请求的数据放入循环中,将data中的数据逐一放入并访问,然后判断B返回的data.list字段是否为空。完整js如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 1. 先拿到接口 A 的 data 数组
const aData = pm.response.json().data; // 假设是 [{combatUnit:'ES-C', ...}, ...]
aData.push("ccc-T")

// 2. 公共请求头(按需写)
const headers = {
'Content-Type': 'application/json',
'authorization': 'xxx'
};

// 3. 轮询调接口 B
aData.forEach(item => {
const cu = item;
if (!cu) return; // 空值跳过

const body = {
attribute1: '',
businessTab: '',
combatUnit: cu,
region: '',
role: '4',
subsidiary: ''
};

pm.sendRequest({
url: 'http://localhost:8095/areaData',
method: 'POST',
header: headers,
body: {
mode: 'raw',
raw: JSON.stringify(body)
}
}, (err, res) => {
if (err) {
console.error('请求失败', cu, err);
return;
}

const json = res.json();
if (!json.data || !json.data.list || json.data.list.length === 0) {
console.log(`⚠️ ${cu} 返回 list 为 null/空`);
}
});
});

然后因为上面全部测试都是有list的,所以我加了一个aData.**push**("ccc-T"),这个数据绝对是没有的,所以send这个接口以后,可以再左下角的Console中看到:

image-20250723134955912

二. Python-单线程

python作为一门编程语言,在写脚本上有其独特的优势,具体过程和javascript在postman中是一样的,但是区别在于接口A需要自己调用一下,区别也不大。

python中的网络编程可以使用requests包,没有的话可以pip install requests下载一下。具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import requests
import json

# 1. 公共请求头
headers = {
"Content-Type": "application/json",
"authorization": "xxx"
}

# 2. 模拟接口 A 返回的数据
body_first = {
"area": None,
"userId": 702
}

url_first = "http://localhost:8095/unit"

a_data = ["nothing"] # 原始 data 数组
try:
resp = requests.post(url_first, headers=headers, data=json.dumps(body_first), timeout=5)
resp.raise_for_status()
a_data = resp.json()
if a_data:
a_data = a_data.get("data",{})
except requests.RequestException as e:
print("请求失败", e)
a_data.append("ccc-T") # 再塞一条

# 3. 轮询调接口 B
url = "http://localhost:8095/areaData"

for item in a_data:
# 兼容 item 可能是 dict 或 str
cu = item.get("combatUnit") if isinstance(item, dict) else item
if not cu:
continue

body = {
"attribute1": "",
"businessTab": "",
"combatUnit": cu,
"region": "",
"role": "4",
"subsidiary": ""
}

try:
resp = requests.post(url, headers=headers, data=json.dumps(body), timeout=5)
resp.raise_for_status()
data = resp.json()
if not data.get("data", {}).get("list"):
print(f"⚠️ {cu} 返回 list 为 null/空")
else:
print(f"{cu} 查询成功")
except requests.RequestException as e:
print("请求失败", cu, e)

最终查询结果如下。

image-20250723140325946

三. Python-多线程

上面python写完后我发现,这个访问实在是慢,postman只要6s,但这python要30s,这能忍?那不得搞个多线程访问一下?

首先先说明一下,我们可以用以下代码计算时间:

1
2
3
4
5
6
import time

start = time.time()
# ……其他代码
end = time.time()
print(f"耗时: {end - start:.2f} 秒")

多线程所用的包concurrent.futures是原生python自带的,因此不需要pip。注意代码中的等待时间可以稍微设置长一点timeout=15,我之前5s,结果老有现成执行不完。

完整的多线程代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import requests
import json
from concurrent.futures import ThreadPoolExecutor, as_completed
import time

start = time.time()

# 线程数:可根据 CPU/IO 情况调整
MAX_WORKERS = 8

headers = {
"Content-Type": "application/json",
"authorization": "xxx"
}

def query_one(combat_unit: str):
"""单个 combatUnit 的请求逻辑"""
body = {
"attribute1": "",
"businessTab": "",
"combatUnit": combat_unit,
"region": "",
"role": "4",
"subsidiary": ""
}
try:
r = requests.post(
"http://localhost:8095/areaData",
headers=headers,
data=json.dumps(body),
timeout=15
)
r.raise_for_status()
data = r.json()
if not data.get("data", {}).get("list"):
return f"⚠️ {combat_unit} 返回 list 为 null/空"
else:
return f"{combat_unit} 查询成功"
except Exception as e:
return f"请求失败 {combat_unit} {e}"

# 1. 先拿到接口 A 的数据(同你原来的写法)
body_first = {"area": None, "userId": 702}
resp = requests.post(
"http://localhost:8095/unit",
headers=headers,
data=json.dumps(body_first),
timeout=5
)
resp.raise_for_status()
a_data = resp.json().get("data", [])
a_data.append("ccc-T")

# 2. 把 a_data 转成纯 combatUnit 字符串列表
units = [
(item.get("combatUnit") if isinstance(item, dict) else item)
for item in a_data
if item
]

# 3. 多线程并发调用
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as pool:
futures = [pool.submit(query_one, u) for u in units if u]
for fut in as_completed(futures):
print(fut.result())

end = time.time()
print(f"耗时: {end - start:.2f} 秒")

最终结果如下:

image-20250723140514080

这样子就能很快的运行了,如果把MAX_WORKERS调大,在我的计算机上可以到20为止,都是上涨的,可以在2.5s左右就运行完,具体得看电脑配置。

四. 总结

Postman 的脚本之所以 6 s 左右就跑完,而 Python 却要 30 s,本质差异在于并发

  1. Postman
    pm.sendRequest()异步非阻塞 的:
    · 循环里只是把请求“发出去”就立即继续下一轮,
    · 所有请求几乎同时到达服务器,
    · 因此耗时 ≈ 最慢的那一次 RTT(往返时延)≈ 6 s。
  2. Python-单线程
    我们用的是 串行阻塞requests.post(...)
    · 一次请求发出去之后,脚本会阻塞在原地等服务器返回,
    · 下一次请求要等上一次完全结束才开始,
    · 假设一次 RTT 也是 6 s,10 条数据就是 10×6 s ≈ 60 s;
    · 现在 30 s,说明可能只有 5 条数据,或者 RTT 在 3 s 左右。

结论:Python 没有并发,请求依次排队,于是总耗时=单条耗时×条数

  • 标题: 利用脚本进行接口测试
  • 作者: Sabthever
  • 创建于 : 2025-07-25 13:29:39
  • 更新于 : 2025-10-09 16:14:31
  • 链接: https://sabthever.cn/2025/07/25/technology/嵌套接口测试脚本/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。