引言
在前面一篇文章介绍了怎么生成测试用例,而在生成的测试用例文件中,我们可以看到很多的属性,那么这些属性代表什么呢?都有什么作用呢?以及怎么用呢?
每个HttpRunner
测试用例是HttpRunner
的子类,并且必须包含config
和teststeps
两个属性。
1. config 属性
配置testcase
级别的config
属性,它包含base_url
、name
、verify
、variables
、export
;
每一个测试用例(testcase)都有一个config
属性,可以是testcase
级别的配置
name
(必需):testcase
的一部分,会显示咋测试报告以及运行日志当中base_url
(可选):功能路径,url的一部分。比如:http://www.woshipm.com/
。如果设置了base_url
的话,在teststep
中的url
中只能设置相对路径。如果需要切换不同的环境的话,该功能很有用。variables
(可选):testcase通用变量。每个step
中可以引用没有在step
中设置的变量。换句话说,step
中设置的变量的级别比在config
中设置的变量级别更高。verify
(可选):指定是否验证服务器的TLS
证书。如果想要记录testcase
中的http
数据特别有用。如果不设置或者设置为true
则会产生SSLError
错误。export
(可选):提取testcase
的session
变量。测试用例是为黑盒,config
中的variables
视为输入,export
为输出。特别是,当该testcase
中的某个输出作为下一个testcase
中的输入的时候特别有用。
2. teststeps 属性
每一个testcase中包括一个或者多个排序的steps
列表(List[Step]
),每个step
相当于一个api
请求(request
)或者testcase
的引用。此外还支持variables/extract/validate/hooks
机制实现更为复杂的场景。
RunRequest(name)
RunRequest在一个步骤中用于向API发出请求,并为响应执行一些提取或验证。RunRequest的参数name作为step的名字,将会在测试报告和运行日志中显示。
.with_variables
teststep
的变量。 每个步骤中的变量都是相互独立的,因此如果想相互共享variable
的话,需要把variable
配置在config
中。另外teststep
中的变量会覆盖config
中相同名称的变量。
.method(url)
指定http
的方法的url
,它对应于requests.request
的方法和url
参数。如果配置中设置了base_url
,则此处之能设置相对路径。
.with_params
指定request url
的参数,这个相当于requests.request
中的params
参数的数据。
.with_headers
指定request
的http headers
。相当于requests.request
的headers
参数部分。
.with_cookies
指定request
的http cookies
。相当于requests.request
的cookies
参数部分。
.with_data
指定request
的http body
。相当于requests.request
的data
参数部分。
.with_json
指定request
的http json
。相当于requests.request
的json
参数部分。
.extract
使用jmespath
提取json
应答数据:.with_jmespath(jmes_path:Text, var_name:Text)-->jmes_path: jmespath
表达式,可以参考https://jmespath.org/tutorial.html
做详细了解;var_name
: 存储提取值的变量名,该变量可以被后续的steps
中直接使用。
.validate
使用jmespath
提取json
应答数据,并使用.assert_xxx(jmes_path:Text, expected_value:Any)
验证期望值。其中jmes_path
表示jmespath
表达式; expected_value
为预期值,变量或者函数表示。如下图:
RunTestCase(name)
在step
中引用RunTestCase
来调用另一个testcase
。参数name
为用来表示testcase
的名字,会展示在运行日志中和测试报告中。
.with_variables
同 RunRequest
的.with_variables
.call
指定引用testcase
的类
.export
指定要从引用的testcase
导出的会话变量名。导出的变量可以被后续的测试步骤引用。
3. 示例分析
以网站的推荐作家接口作为示例:
示例网站:http://www.woshipm.com
然后拿到第一个作者的作者ID作为参数查看作者内容;
这个测试用例包含两个api请求:
/api2/recommendation/authors/with-article-info
/api2/user/followings/status
使用Step
调用,代码如下:
# NOTE: Generated By HttpRunner v3.1.4
# FROM: har\woshipm_recommended_author.har
from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase
class TestCaseWoshipmRecommendedAuthor(HttpRunner):
config = Config("request methods testcase with functions")\
.variables(
**{
"pn": "1",
"ps": "20"
}
)\
.base_url("http://www.woshipm.com")\
.verify(False)\
.export(*["author_id"])
teststeps = [
Step(
RunRequest("/api2/recommendation/authors/with-article-info")
.get("/api2/recommendation/authors/with-article-info")
.with_params(**{"PN": "${pn}", "PS": "${ps}"})
.with_headers(
**{
"Host": "www.woshipm.com",
"Accept": "application/json, text/plain, */*",
"COMMON_ACCESS_TOKEN_SECRET": "NjM4ODgxLCwxNjI4MzQyNjAzOTM1LCxodHRwczovL3N0YXRpYy5xaWRpYW5sYS5jb20vd29zaGlwbV9kZWZfaGVhZF8xLmpwZywseWMsLDRkNDI0MzM0Y2U2NDQ2NDI5MThlNjFiZjNlZmQ5NjgzLTAtMQ==",
"COMMON_ACCESS_TOKEN": "MGJRTW5TU0xiU3M0TEY1QUg0N1NnTFhsWXVoYm5nOFlrbVM5MEpRbDEzaTI0TTk4NHFhTEZMSW1YZ0lnNEw0WU0wMXdhbVJFYTBmNnNrNDJpQUZRcG4xZXFwWHQwc3ZoL0Y2U0dhMHM0WTRrZ3RGU2dYMDM0WEU2OGU4b3FRTDA=",
"X-WP-Nonce": "452000896a",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
"Referer": "http://www.woshipm.com/users",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9",
"Cookie": "new_device_id=device_0ca45ac5-91b5-48f1-989f-09d72908c13f; isOverlayShowx=VVVV; Hm_lvt_b85cbcc76e92e3fd79be8f2fed0f504f=1628163442,1628172667,1628334320; dts_device_info=%7B%22device_id%22%3A%22device_0ca45ac5-91b5-48f1-989f-09d72908c13f%22%2C%22device_brand%22%3A%22%22%2C%22device_model%22%3A%22%22%2C%22client_type%22%3A%22pc%22%2C%22os_type%22%3A%22Win32%22%2C%22os_version%22%3A%22%22%2C%22network_type%22%3A%22%22%2C%22browser_type%22%3A%22Chrome%22%2C%22browser_version%22%3A%2291.0.4472.124%22%7D; article_index=; article_user_profile=article_recom_users@author_items; article_detail_normal=article_user_profile@article_items; _DAU=done%21; wordpress_logged_in_41071e66c7856d347ba575b5339c5a86=18144854885%7C1629552204%7CcnJRMC1LLCPnvL3ZEGLxFpuh3LXe1JeyovQ8OlsPvm6%7C3b42d3281e8bef80c1f7ef7c44ff0b7430bed59c80ff13f7e97cb93c3805feee; t=MGJRTW5TU0xiU3M0TEY1QUg0N1NnTFhsWXVoYm5nOFlrbVM5MEpRbDEzaTI0TTk4NHFhTEZMSW1YZ0lnNEw0WU0wMXdhbVJFYTBmNnNrNDJpQUZRcG4xZXFwWHQwc3ZoL0Y2U0dhMHM0WTRrZ3RGU2dYMDM0WEU2OGU4b3FRTDA%3D; s=NjM4ODgxLCwxNjI4MzQyNjAzOTM1LCxodHRwczovL3N0YXRpYy5xaWRpYW5sYS5jb20vd29zaGlwbV9kZWZfaGVhZF8xLmpwZywseWMsLDRkNDI0MzM0Y2U2NDQ2NDI5MThlNjFiZjNlZmQ5NjgzLTAtMQ%3D%3D; firstVisitTime=1628342605147; post_view_4751263=2; Hm_lpvt_b85cbcc76e92e3fd79be8f2fed0f504f=1628342635",
"Connection": "keep-alive",
}
)
.with_cookies(
**{
"new_device_id": "device_0ca45ac5-91b5-48f1-989f-09d72908c13f",
"isOverlayShowx": "VVVV",
"Hm_lvt_b85cbcc76e92e3fd79be8f2fed0f504f": "1628163442,1628172667,1628334320",
"dts_device_info": "%7B%22device_id%22%3A%22device_0ca45ac5-91b5-48f1-989f-09d72908c13f%22%2C%22device_brand%22%3A%22%22%2C%22device_model%22%3A%22%22%2C%22client_type%22%3A%22pc%22%2C%22os_type%22%3A%22Win32%22%2C%22os_version%22%3A%22%22%2C%22network_type%22%3A%22%22%2C%22browser_type%22%3A%22Chrome%22%2C%22browser_version%22%3A%2291.0.4472.124%22%7D",
"article_index": "",
"article_user_profile": "article_recom_users@author_items",
"article_detail_normal": "article_user_profile@article_items",
"_DAU": "done%21",
"wordpress_logged_in_41071e66c7856d347ba575b5339c5a86": "18144854885%7C1629552204%7CcnJRMC1LLCPnvL3ZEGLxFpuh3LXe1JeyovQ8OlsPvm6%7C3b42d3281e8bef80c1f7ef7c44ff0b7430bed59c80ff13f7e97cb93c3805feee",
"t": "MGJRTW5TU0xiU3M0TEY1QUg0N1NnTFhsWXVoYm5nOFlrbVM5MEpRbDEzaTI0TTk4NHFhTEZMSW1YZ0lnNEw0WU0wMXdhbVJFYTBmNnNrNDJpQUZRcG4xZXFwWHQwc3ZoL0Y2U0dhMHM0WTRrZ3RGU2dYMDM0WEU2OGU4b3FRTDA%3D",
"s": "NjM4ODgxLCwxNjI4MzQyNjAzOTM1LCxodHRwczovL3N0YXRpYy5xaWRpYW5sYS5jb20vd29zaGlwbV9kZWZfaGVhZF8xLmpwZywseWMsLDRkNDI0MzM0Y2U2NDQ2NDI5MThlNjFiZjNlZmQ5NjgzLTAtMQ%3D%3D",
"firstVisitTime": "1628342605147",
"post_view_4751263": "2",
"Hm_lpvt_b85cbcc76e92e3fd79be8f2fed0f504f": "1628342635",
}
)
.extract()
.with_jmespath("body.REQUEST_ID", "request_id")
.with_jmespath("body.RESULT.authors[0].authorId", "author_id")
.validate()
.assert_equal("status_code", 200)
.assert_equal('headers."Content-Type"', "application/json;charset=UTF-8")
.assert_equal("body.CODE", 200)
.assert_equal("body.MESSAGE", "请求成功")
.assert_equal("body.REQUEST_ID", "${request_id}")
.assert_equal("body.HOST_ID", "localhost")
),
Step(
RunRequest("/api2/user/followings/status")
.get("/api2/user/followings/status")
#.with_params(**{"authorIds": "801994"})
.with_params(**{"authorIds": "${author_id}"})
.with_headers(
**{
"Host": "www.woshipm.com",
"Accept": "application/json, text/plain, */*",
"COMMON_ACCESS_TOKEN_SECRET": "NjM4ODgxLCwxNjI4MzQyNjAzOTM1LCxodHRwczovL3N0YXRpYy5xaWRpYW5sYS5jb20vd29zaGlwbV9kZWZfaGVhZF8xLmpwZywseWMsLDRkNDI0MzM0Y2U2NDQ2NDI5MThlNjFiZjNlZmQ5NjgzLTAtMQ==",
"COMMON_ACCESS_TOKEN": "MGJRTW5TU0xiU3M0TEY1QUg0N1NnTFhsWXVoYm5nOFlrbVM5MEpRbDEzaTI0TTk4NHFhTEZMSW1YZ0lnNEw0WU0wMXdhbVJFYTBmNnNrNDJpQUZRcG4xZXFwWHQwc3ZoL0Y2U0dhMHM0WTRrZ3RGU2dYMDM0WEU2OGU4b3FRTDA=",
"X-WP-Nonce": "452000896a",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
"Referer": "http://www.woshipm.com/u/801994",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9",
"Cookie": "new_device_id=device_0ca45ac5-91b5-48f1-989f-09d72908c13f; isOverlayShowx=VVVV; Hm_lvt_b85cbcc76e92e3fd79be8f2fed0f504f=1628163442,1628172667,1628334320; dts_device_info=%7B%22device_id%22%3A%22device_0ca45ac5-91b5-48f1-989f-09d72908c13f%22%2C%22device_brand%22%3A%22%22%2C%22device_model%22%3A%22%22%2C%22client_type%22%3A%22pc%22%2C%22os_type%22%3A%22Win32%22%2C%22os_version%22%3A%22%22%2C%22network_type%22%3A%22%22%2C%22browser_type%22%3A%22Chrome%22%2C%22browser_version%22%3A%2291.0.4472.124%22%7D; article_index=; article_user_profile=article_recom_users@author_items; article_detail_normal=article_user_profile@article_items; _DAU=done%21; wordpress_logged_in_41071e66c7856d347ba575b5339c5a86=18144854885%7C1629552204%7CcnJRMC1LLCPnvL3ZEGLxFpuh3LXe1JeyovQ8OlsPvm6%7C3b42d3281e8bef80c1f7ef7c44ff0b7430bed59c80ff13f7e97cb93c3805feee; t=MGJRTW5TU0xiU3M0TEY1QUg0N1NnTFhsWXVoYm5nOFlrbVM5MEpRbDEzaTI0TTk4NHFhTEZMSW1YZ0lnNEw0WU0wMXdhbVJFYTBmNnNrNDJpQUZRcG4xZXFwWHQwc3ZoL0Y2U0dhMHM0WTRrZ3RGU2dYMDM0WEU2OGU4b3FRTDA%3D; s=NjM4ODgxLCwxNjI4MzQyNjAzOTM1LCxodHRwczovL3N0YXRpYy5xaWRpYW5sYS5jb20vd29zaGlwbV9kZWZfaGVhZF8xLmpwZywseWMsLDRkNDI0MzM0Y2U2NDQ2NDI5MThlNjFiZjNlZmQ5NjgzLTAtMQ%3D%3D; firstVisitTime=1628342605147; post_view_4751263=2; Hm_lpvt_b85cbcc76e92e3fd79be8f2fed0f504f=1628342649",
"Connection": "keep-alive",
}
)
.with_cookies(
**{
"new_device_id": "device_0ca45ac5-91b5-48f1-989f-09d72908c13f",
"isOverlayShowx": "VVVV",
"Hm_lvt_b85cbcc76e92e3fd79be8f2fed0f504f": "1628163442,1628172667,1628334320",
"dts_device_info": "%7B%22device_id%22%3A%22device_0ca45ac5-91b5-48f1-989f-09d72908c13f%22%2C%22device_brand%22%3A%22%22%2C%22device_model%22%3A%22%22%2C%22client_type%22%3A%22pc%22%2C%22os_type%22%3A%22Win32%22%2C%22os_version%22%3A%22%22%2C%22network_type%22%3A%22%22%2C%22browser_type%22%3A%22Chrome%22%2C%22browser_version%22%3A%2291.0.4472.124%22%7D",
"article_index": "",
"article_user_profile": "article_recom_users@author_items",
"article_detail_normal": "article_user_profile@article_items",
"_DAU": "done%21",
"wordpress_logged_in_41071e66c7856d347ba575b5339c5a86": "18144854885%7C1629552204%7CcnJRMC1LLCPnvL3ZEGLxFpuh3LXe1JeyovQ8OlsPvm6%7C3b42d3281e8bef80c1f7ef7c44ff0b7430bed59c80ff13f7e97cb93c3805feee",
"t": "MGJRTW5TU0xiU3M0TEY1QUg0N1NnTFhsWXVoYm5nOFlrbVM5MEpRbDEzaTI0TTk4NHFhTEZMSW1YZ0lnNEw0WU0wMXdhbVJFYTBmNnNrNDJpQUZRcG4xZXFwWHQwc3ZoL0Y2U0dhMHM0WTRrZ3RGU2dYMDM0WEU2OGU4b3FRTDA%3D",
"s": "NjM4ODgxLCwxNjI4MzQyNjAzOTM1LCxodHRwczovL3N0YXRpYy5xaWRpYW5sYS5jb20vd29zaGlwbV9kZWZfaGVhZF8xLmpwZywseWMsLDRkNDI0MzM0Y2U2NDQ2NDI5MThlNjFiZjNlZmQ5NjgzLTAtMQ%3D%3D",
"firstVisitTime": "1628342605147",
"post_view_4751263": "2",
"Hm_lpvt_b85cbcc76e92e3fd79be8f2fed0f504f": "1628342649",
}
)
.extract()
.with_jmespath("body.REQUEST_ID", "request_id")
.validate()
.assert_equal("status_code", 200)
.assert_equal('headers."Content-Type"', "application/json;charset=UTF-8")
.assert_equal("body.CODE", 200)
.assert_equal("body.MESSAGE", "请求成功")
.assert_equal("body.REQUEST_ID", "${request_id}")
.assert_equal("body.HOST_ID", "localhost")
)
]
if __name__ == "__main__":
TestCaseWoshipmRecommendedAuthor().test_start()
在示例中:
-
config
属性- 使用
config
属性抽离base_url
,以便切换环境IP
的问题; - 在
config
中使用.variables
声明全局变量 - 在
config
使用export
提取测试用例的第一个step
的作者ID:author_id
作为下一个step
中的输入;
- 使用
-
teststeps
属性-
.extract().with_jmespath("body.REQUEST_ID", "request_id")
提取请求响应数据,因为在响应的request_id
是动态的,所以在验证中需要先存储提取值的变量名,该变量可以被后续的steps中直接使用,也可以被assert_equal
使用。 -
.extract().with_jmespath("body.RESULT.authors[0].authorId", "author_id")
提取请求
/api2/recommendation/authors/with-article-info
响应数据的authors
列表中的第一个作者的作者ID:authorId
并声明为author_id
,然后在config
被author_id
引用,之后在发送请求
/api2/user/followings/status
的时候通过config
的.export
方法将author_id
的值传入进来; -
.with_params(**{"PN": "${pn}", "PS": "${ps}"})
调用config
属性的.variables
里面声明的pn
以及ps
两个参数;
-