httprunner

发布时间 2023-07-24 17:42:15作者: 好像条狗

httprunner
(仅作为个人笔记,如有雷同,请联系删除。。)
https://www.cnblogs.com/aichixigua12/p/13162479.html

HttpRunner 是一款面向 HTTP(S) 协议的通用测试框架,只需编写维护一份 YAML/JSON 脚本,即可实现自动化测试、性能测试、线上监控、持续集成等多种测试需求。

1、特点:
继承 Requests 的全部特性,轻松实现 HTTP(S) 的各种测试需求
采用 YAML/JSON 的形式描述测试场景,保障测试用例描述的统一性和可维护性
借助辅助函数(debugtalk.py),在测试脚本中轻松实现复杂的动态计算逻辑
支持完善的测试用例分层机制,充分实现测试用例的复用
测试前后支持完善的 hook 机制
响应结果支持丰富的校验机制
基于 HAR 实现接口录制和用例生成功能(har2case)
结合 Locust 框架,无需额外的工作即可实现分布式性能测试
执行方式采用 CLI 调用,可与 Jenkins 等持续集成工具完美结合
测试结果统计报告简洁清晰,附带详尽统计信息和日志记录
极强的可扩展性,轻松实现二次开发和 Web 平台化
2、安装:
pip install httprunner,安装完成后cmd命令检查:hrun -V

可以用相关命令直接生成对应的项目文件夹:hrun --startproject 文件夹(如HttpRunnerDemo),执行命令后可以看到生成对应的目录,在当前目录下。
报错解决:原因是框架的版本升级了。
解决方案:去掉-- ,直接用 httprunner startproject HttpRunnerDemo 来创建项目。(hrun不好使)

 

文件目录结构说明:

(1). YAML/JSON:(必须)测试用例文件,一个文件对应一条测试用例。
(2). debugtalk:(可选)脚本函数,存储项目中逻辑运算函数,该文件存在时,将作为项目定位标记,其所在目录被视为项目工程的根路径,该文件不存在时,运行测试的路径将被视为当前工作目录,测试用例文件中的相对路径,如.csv均需基于当前工作目录,运行测试后,测试报告文件夹reports会生成在当前工作目录
(3). .env:(可选)存储项目环境变量
(4). reports、logs:(自动生成)运行后自动生成,无需创建

3、YAML文件:作为测试脚本文件。
YAML文件可以创建为.yml文件,.yml需遵循严格的缩进。

基本语法:
(1). 格式要求:K:(空格) v,表示一对键值对,必须要写空格,#表示注释
(2). 以空格的缩进来控制层级关系,只要是左对齐的一列数据,都是同一个层级的
(3). 属性和值大小写敏感
(4). 缩进不能用tab键,手动空格

注意:
(1). 对于单个YAML/JSON文件来说,数据存储结构为 list of dict的形式,其中可能包含一个全局配置项(config)和若干个测试步骤(test);
(2). HttpRunner划分了两层变量空间作用域(context)

config:作为整个测试用例的全局配置项;

test:测试步骤的变量空间(context)会继承或覆盖config中定义的内容

注意:若某变量在config中定义了,在某test中没有定义,则该test会继承该变量;若某变量在config和test中都定义了,则该test使用config中定义的变量值,各个测试步骤的变量空间相互堵路,互不影响,如需在多个测试步骤中传递参数值,则需要使用extract关键字,并且只能从前往后传递

eg:在testcases中建立一个demo的yml文件,把接口测试资料按一定的规则写入:

注:config中引入的变量:很多接口的公共部分,可以抽离出来放入debugtalk.py

- config: # config是接口配置部分
request:
headers: $content # 在config中引入变量$host、$content等
base_url: $host
url: /commons_service/weather/getWeatherListByLocation?appType=6&requestId=777777
method: POST

- test: # test是用例部分
name: test weather ID1
request:
json:
body: {"lat":"", "lng":"", "city":"武汉市"}
validate: # validate是断言部分
- eq: [status_code,200]
- eq: [json.msg,"成功"]

- test:
name: test weather ID2
request:
json:
body: { "lat":"123","lng":"125","city":""}
validate:
- eq: [status_code,200]
- eq: [json.msg,"成功"]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
4、运行case:
hrun HttpRunnerDemo\testcases\demo_testcase_ref.yml


另还会在logs下生成测试日志: HttpRunnerDemo\logs\77343183-2374-4d6a-a499-6580fbb89b45.run.log,可以打开log查看详情,可以清晰的看到接口返回和断言结果。

注意:感觉是在运行时会把yaml测试文件转换成对应的_test.py文件,可以关注下。


5、httprunner还支持接口录制
(用抓包工具生成.har文件)后转为可执行的用例(用har2case),最重要的是,测试用例和代码的分离。这样使得稍有编码功底的人迅速上手。

6、简单的接口测试场景:
利用HttpRunner发送请求–(get/post,带参数/不带参数)

eg:伪代码,不执行

### 1.get请求,不带参数,并对响应结果正则表达式提取做断言
- config:
name: "get请求,不带参数"
base_url: http://www.baidu.com

- test:
name: "open xxx.."
request:
url: /okhll/
method: GET
extract:
- code: "status_code"
- title: 'tid=72872&fid="89" class="st" style="" title="测试">(.+?)</a>'
validate:
- eq: [$code, 200]
- eq: [$title, '测试']


### 2.get请求,带参数
- config:
name: "get请求,带参数"
base_url: http://www.baidu.com

- test:
name: "get access_token"
request:
url: /cgi_bin/token
method: GET
params:
grant_type: 'clcoxxxxxxxxxsla'
appid: 'wk9jh92m83'
secret: 'a5d68699c76d986b766e282367378c7627c6c7'
extract:
- access_token: content.access_token
validate:
- eq: ["status_code", 200]
- eq: [content.expires_in, 7200]


### 3.模拟请求头
- config:
name: "发送模拟请求头的请求"
base_url: "https://www.baidu.com"

- test:
name: 请求的对象是百度
request:
url: /
method: GET
headers:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
User-Agetn: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36
validate:
- eq: [status_code, 200]


### 4.post请求
- config:
name: 发送post请求
basr_url: https://www.baidu.com

- test:
name: 发送post请求
request:
url: /cgi-bin/tags/create
method: POST
params:
access_token: $access_token
json: {
"open_id": "kshdu7236n1928n17631mh",
"remark": "luoks"
}
validate:
- eq: ['status_code', 200]
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
69
70
71
72
73
74
75
76
7、参数说明:
config参数:


(1). output的用法(也可以用export代替):其作用是输出值,相当于print()函数的输出功能,另一个作用则是可以定义变量时用,经常与extract关键字一起组合使用

- config:
name: "访问百度首页"
base_url: www.baidu.com
output:
- code
- title

- test:
name: "open baidu first-page"
request:
url: /phpuwk/
method: GET
extract:
- code: "status_code"
- title: 'tid=72872&fid="89" class="st" style="" title="测试">(.+?)</a>'
validate:
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
test参数:


(1). extract关键字:可以提取的关键字为:status_code, cookies, elapsed, headers, content, text, json, encoding, ok, reason, url。支持多种提取方式:

响应结果为 JSON 结构:可采用==.运算符==的方式,例如headers.Content-Type、content.success
响应结果为 text/html 结构:可采用正则表达式的方式,例如blog-motto\">(.*)
(2). request 关键词:包括http请求中的详细内容

headers:请求头部信息
method: 请求方式
url:请求地址
host:请求主机地址
params:GET请求参数
data:表单形式的参数
json:json格式的参数
8、断言:
validate关键字:拿实际结果和期望结果去比较。支持两种格式:
(1). {"comparator_name": [check_itrm, expect_value]}
(2). {'check': check_itrm, 'comparator': comparator_name, 'expect': expect_value}
举例:

- config:
name: "get请求,不带参数"
base_url: http://www.baidu.com

- test:
name: "open xxx.."
request:
url: /okhll/
method: GET
validate:
- {'check': status_code, 'comparator': eq, 'expect': 200}
1
2
3
4
5
6
7
8
9
10
11


contains关键字:判断包含的断言。

使用:- contains: [content, 响应正文中的值],判断响应正文中是否包含了某字符串

举例:

- config:
name: "get请求,带参数"
base_url: http://www.baidu.com

- test:
name: "get access_token"
request:
url: /cgi_bin/token
method: GET
params:
grant_type: 'clcoxxxxxxxxxsla'
appid: 'wk9jh92m83'
secret: 'a5d68699c76d986b766e282367378c7627c6c7'
extract:
- access_token: content.access_token
validate:
- contains: [content, access_token]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
9、变量的创建及使用:
定义变量:variables关键字定义,export也会定义变量【一个test中赋值之后,可被其他test通过$引用】
使用变量:$变量名 ,如: $name
10、传参操作:
test中测试步骤之间传参:

- config:
name: "访问xxx"
base_url: www.xxx.com
export:
- token # 2. 控制台输出变量token的值

- test:
name: "获取access_token"
request:
url: /phpuwk/
method: GET
params:
xxx: xxx
extract:
- token: content.access_token # 1. extract提取access_token的值,并赋值给变量token
validate:
...

- test:
name: 查看已经被创建的标签
request:
url: /okhll/
method: GET
headers:
content-type: alication/json
params:
access_token: "$token" # 3. 引用token值
validate:
...
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
.env文件:存放设置的全局变量参数及参数值,引用方式为: ${ENV(参数名)}

## .env
url = https://api.weixin.qq.com

## xxx.yml
- config:
name: "引用.env文件传递参数"
base_url: ${ENV(url)}
...
1
2
3
4
5
6
7
8
debugtalk.py文件:可在这个文件中编写辅助函数,然后进行关联。引用该文件中的函数的方法为:${函数名}

## debugtalk.py
#! /usr/bin/env_python
# encoding: utf-8
import requests
def get_token():
get_data={"grant_type":"xxx",
"appid":"xxx",
"secret":"xxxxxxx"}
respone = requests.get(url='https://api.weixin.qq.com/cgi-bin/token', params=get_data)
return respone.json()['access_token']

## xxx.yml
...
- test:
name: "引用debugtalk.py文件函数传递参数"
request:
url: /cgi-bin/tags/get
method: GET
headers:
content-type: alication/json
params:
access_token: ${get_token()} ## 引用debugtalk.py文件函数传递参数
validate:
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
用例之间传参:把需要的值都给参数化,然后进行相互调用,可以提高代码复用率
举例:【注意:这几个过程中,变量名必须保持一致】
(1). 先在test中用extrac关键字提取出token赋值给token_id;
(2). 然后config中用export关键字输出token_id值;
(3). 其他test需要引用的情况下,需要先使用variables关键字定义该变量然后再引用

## test1.yaml
- config:
name: "访问xxx"
base_url: www.xxx.com
export:
- token_id # 2. 控制台输出token_id

- test:
name: "获取access_token"
request:
url: /phpuwk/
method: GET
params:
xxx: xxx
extract:
- token_id: content.access_token # 1. 提取并赋值access_token给token_id
validate:
...


## test2.yaml
- config:
name: "get请求,带参数"
base_url: ${ENV(url)}

- test:
name: "调用获取access_token的接口,引用其中的access_token参数"
testcase: testcases/test1.yaml # 执行test1.yaml,方便引用其中的access_token参

- test:
variables:
token_id: $token_id # 3. 定义变量
name: 查看已经被创建的标签
request:
url: /okhll/
method: GET
headers:
content-type: alication/json
params:
access_token: $token_id # 4. 引用变量
validate:
- {'check': status_code, 'comparator': eq, 'expect': 200}
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
11、参数化:
通过testsuite中编写parameter关键字参数化:从2.0.0版本开始,HttpRunner不再支持在测试文件中进行参数化配置,参数化的功能需要在testsuite中实现,实现的方式为parameters下面写参数,testsuite中执行的时候需要引用testcases中的用例,参数化后需要在testsuite中运行yml文件

### testsuit.yml 文件
config:
name: 参数化测试套件
testcases:
- name: 这里引用testcases中的测试用例
testcase: testcases/test01.yml # ----------> 测试用例的绝对路径
parameters: # ---------------------------> 设置参数
serch_word: [baidu, 777, python] # ------> 参数名:参数列表


### test01.yml 文件
- config:
name: 测试用例,被上面测试套件引用
base_url: https://www.baidu.com
export:
- title
- test:
name: 百度搜索
request:
url: /s
method: GET
params:
wd: $serch_word # ---------------------> 对testsuit中parameters设置的参数的引用
header:
Accept: xxx
Accept-Encoding: xxx
Accept-Language: xxx
User-Agent: xxx
extract:
- title: <title>(.+?)</title>
validate:
-eq: ["status_code", 200]
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
(1). testsuite传递多个参数:以-隔开参数,以列表的方式处理【一个list:是一组参数,传递给多个变量;一个-:case执行一次,所谓参数化】

### testsuit.yml 文件
config:
name: 参数化测试套件
#testcases:
# - name: 这里引用testcases中的测试用例
# testcase: testcases/test01.yml # ----------> 测试用例的绝对路径
# parameters: # ---------------------------> 设置参数
# serch_word: [baidu, 777, python] # ------> 参数名:参数列表
testcases:
- name: 这里引用testcases中的测试用例
times: 5
skipIf: [1, 2]
testcase: testcases/test02.yml # ----------> 测试用例的绝对路径
parameters: # ---------------------------> 设置参数
serch_word-result: # --------------> 以-隔开参数,以列表的方式处理:参数1:serch_word,参数2:result
- [python, python_百度搜索] # ----------> 第一组参数:[参数1,参数2]
- [chrome, chrome_百度搜索] # ----------> 第二组参数
- [google, google_百度搜索] # ----------> 第三组参数


### test02.yml 文件
- config:
name: 测试用例,被上面测试套件引用
base_url: https://www.baidu.com
export:
- title
- test:
name: 百度搜索
extract:
- title: <title>(.+?)</title>
request:
url: /s
method: GET
params:
wd: $serch_word # ---------------------> 引用第一个参数:即-的前半部分,serch_word
header:
Accept: xxx
Accept-Encoding: xxx
Accept-Language: xxx
User-Agent: xxx
validate:
-eq: [$title, $result] # -----------------> 引用第二个参数:即-的后半部分,result
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
(2). 笛卡尔积的运用:比如测试账号有四种[“test1”, “test2”, “test3”, “test4”],密码也有四种 [“123456”, “123456”, “123456”, “123456”],用笛卡尔积组合的话,就是4*4=16种组合

### testsuit.yml 文件
config:
name: 参数化测试套件
testcases:
- name: 这里引用testcases中的测试用例
times: 5
skipIf: [1, 2]
testcase: testcases/test02.yml # ----------> 测试用例的绝对路径
parameters: # ---------------------------> 设置参数
user: ["test1", "test2", "test3", "test4"] #
passwd: ["123456", "123456", "123456", "123456"] # case中传入$user和$passwd,case会执行16种组合的次数
1
2
3
4
5
6
7
8
9
10
11
利用csv文件进行参数化:根路径下新建data文件夹–新建info.csv文件,文件中的参数用" , "符号分隔,testsuit中通过P()函数进行引用,case里面需要csv文件中的参数时,则通过 $ 参数名的方法调用,与csv中的参数排列顺序无关,注意参数名称必须与csv表头保持一致

### E:\Code_local\HttpRunnerDemo\data\info.csv
search_word,result # 搜索参数,预期结果
taobao,taobao_百度搜索
python,python_百度搜索
123456,123456_百度搜索
charme,charme_百度搜索
google,google_百度搜索


### testsuit.yml
config:
name: 参数化测试套件
testcases:
- name: 这里引用testcases中的测试用例
testcase: testcases/test02.yml
parameters:
search_word-result: ${P(data/info.csv)} # 引用csv文件,csv参数的个数通过-隔开


### test01.yml
- config:
name: 测试用例,被上面测试套件引用
base_url: https://www.baidu.com
export:
- title
- test:
name: 百度搜索
request:
url: /s
method: GET
params:
wd: $serch_word # ------------> 引用第一个参数:即-的前半部分,serch_word
header:
Accept: xxx
Accept-Encoding: xxx
Accept-Language: xxx
User-Agent: xxx
extract:
- title: <title>(.+?)</title>
validate:
-eq: [$title, $result]
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
通过debugtalk实现参数化:(相当于jmeter中的函数助手的功能),与parameters下引用csv一样,debugtalk参数化的数据也是一个列表,使用${函数}引用debugtalk中的函数

### debugtalk.py
# "随机在某最小值和某最大值的范围内生成多少个整数组成的列表"
def get_randomint(min, max, count=None):
randomint_list = []
for i in range(count):
randomint_list.append(random.randint(min, max))
return randomint_list

def setup_case(case_name):
print("测试用例 s% 开始执行" % case_name)
def teardown_case(case_name):
print("测试用例 s% 执行结束" % case_name)
def setup_step(case_step):
print("测试步骤 s% 开始执行" % case_step)
def teardown_step(case_step):
print("测试步骤 s% 执行结束" % case_step)

if __name__ == "__main__":
print(get_randomint(1, 20, 10))


### testsuit.yml
config:
name: 参数化测试套件
testcases:
- name: 这里引用testcases中的测试用例
testcase: testcases/test01.yml
parameters:
search_word: ${get_randomint(1, 20, 5)} # 引用debugtalk中的函数


### test01.yml
- config:
name: 测试用例,被上面测试套件引用
base_url: https://www.baidu.com
- test:
name: 百度搜索
request:
url: /s
method: GET
params:
wd: $serch_word
header:
...
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
12、跳过执行:只能在testcases中设置
无条件跳过:skip
条件为真跳过:skipIf
条件为假跳过:skipUnless
### test01.yml
- config:
name: 测试用例,被测试套件引用
base_url: https://www.baidu.com
- test:
skip: 无条件跳过 # ----------> a. 无条件跳过:skip
name: 百度搜索
request:
...
validate:
-eq: ["status_code", 200]

- test:
skipIf: 百度搜索2 # ---------> a. 条件为真跳过:skipIf,非空、非0、True时跳过执行
name: 百度搜索2
request:
...
validate:
-eq: ["status_code", 200]

- test:
skipUnless: # ----------> a. 条件为假跳过:skipUnless,空、0、False时跳过执行
name: 百度搜索3
request:
...
validate:
-eq: ["status_code", 200]
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
13、重复执行:times 参数,用例中可以设置用例运行的次数
### test01.yml
- config:
name: 测试用例,被测试套件引用
base_url: https://www.baidu.com
- test:
skipUnless: 1
times: 5 # -----------> times设置case运行次数
name: 百度搜索
request:
...
validate:
-eq: ["status_code", 200]
1
2
3
4
5
6
7
8
9
10
11
12
14、用例分层:
Httprunner项目分层为 api接口层,testcases用例层,testsuite测试套件层


api接口层:不用在里面加特殊细节的断言,断言接口能否请求通即可。新建api文件夹,下面新建一个yml文件进行接口api的编写。举例:

### E:\Code_local\HttpRunnerDemo\api\test.yml
name: "获取access_token"
base_url: ${ENV(url)}

request:
url: /cgi-bin/token
method: GET
params:
grant_type: ${ENV(grant_type)}
appid: ${ENV(appid)}
secret: ${ENV(secret)}
validate:
- eq: [status_code, 200]
1
2
3
4
5
6
7
8
9
10
11
12
13
testcases用例层:直接调用接口层中的yml文件执行,细节断言可在这里编写,同时也可以调用其他用例或者其他用例的参数执行。

### test01.yml
- config:
name: case中调用api文件中的yml文件执行
- test:
name: step01:执行获取token的接口
api: api/test.yml # ------------------> 只需要写被调用的api目录下的yml绝对路径即可
validate:
- contains: [content, access_token]
1
2
3
4
5
6
7
8
testsuite测试套件层:新建testsuites目录存放测试套件,将测试用例整合起来执行

### testsuit01.yml
config:
name: 测试套件整合测试用例进行执行

testcases:
- name: 调用testcases中的用例执行
testcase: testcases/test01.yml # ----> 被调用case的绝对路径
1
2
3
4
5
6
7
15、case之间的相互调用:
可以通过调取用例和调取用例之中的参数进行调用

用例之间的简单调用:testcase关键字,直接给被调用用例的绝对路径

### test01.yml
- config:
name: 测试用例,被测试套件引用
base_url: https://www.baidu.com

- test:
name: 调用其他用例
testcase: testcases/test02.yml # -------> 被调用用例的绝对路径

- test:
...
1
2
3
4
5
6
7
8
9
10
11
用例之间传参的调用:参考【10.4:用例之间传参】
————————————————
版权声明:本文为CSDN博主「你们的好朋友大强」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42753043/article/details/123969056