Httprunner作者是debugtalk,是一个资深的开发者,目前开发httprunner已有5个版本
Httprunner用法比较简单,并且符合工作中的多种场景:
1、做完接口自动化,要求继续做此接口的性能测试(hrun内置locust模块,可以直接做压测)
2、需要做的接口过多,而且只需要替换部分的参数,接口自动化的工作就会完成(charles录制并导出.har文件,通过hrun命令转成.py文件)
3、request库的每一步的接口响应,都需要加断言,而hrun会自动生成断言方法
4、hrun集成的第三方库:loguru、jmespath、pytest
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple httprunner
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple allure-pytest
httprunner -V
httprunner --help
httprunner startproject PartsService
cd PartsService
hmake testcases
方式一: pytest [filename]
方式二: hrun [filename]
方式三: httprunner run [filename]
方式一: pytest [dirname]
方式二: hrun [dirname]
方式三: httprunner run [dirname]
har2case -h
allure:存放allure的报告结果集
api_data:存放接口的请求数据(可以是json文件、xlsx文件、yaml文件)
api_testing:存放api测试用例
api_util:便于api测试的封装方法
common:公共方法,比如读取数据库文件
config:存放全局配置文件
databases:存放数据库文件
login:登录解耦
[base]
# the url domain you wants to use in UI testing
backend_url =https://wwww.baidu.cn/
[report]
### pytest xxxx --alluredir ./report
### allure serve report
### hrun test_post_api.yml --alluredir=allure
### allure generate ./allure/ -o ./reports --clean
backend_url为项目地址,如果有多套环境,可以放多个url来进行切换
import configparser
import os
class ConfigReader:
def __init__(self, file_name):
self.config = configparser.ConfigParser()
config_path = os.path.split(os.path.realpath(__file__))[0] + os.sep + file_name
self.config.read(config_path, "utf-8")
def get_config(self, config_item):
sections = self.config.sections()
for section in sections:
if self.config.has_option(section, config_item):
return self.config.get(section, config_item)
def get_config_by_section(self, config_item, section_name):
return self.config.get(section_name, config_item)
这里只写了一个读取Config文件方法,便于切换多套数据库环境,后续有需要可以做补充
from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase # todo:导入httprunner模块
def get_base_url(): # todo:封装project_host方法,为项目全局的API路径
from common import config_reader # todo: config_reader 引用 config_reader方法
return config_reader.ConfigReader('../config/base_config.cfg').get_config('backend_url')
class TestCaseLogin(HttpRunner): # todo: 定义类名要以Test开头
config = Config("testcase description")\
.verify(False)\ # todo:忽略Https证书
.variables(**{
"Accept":"application/json, text/plain, */*",
"Cookie":"SIAM_IMAGE_CODE=762410696575033344; SIAMTGT=TGT-892-4xraUmB3mrsA0MtWv11ENoZmq1t4CiBYgSGPJjG9NQrHpTqczc-SIAM; LtpaToken=AAECAzYxRjBCN0Y5NjFGMTYwQjlDTj1iaWVzb25nbGluL089Zm90b25UaDZzgyb50ZEjaw/jBYfTHXONKQ==",
"userName": " xxxx",
"password": "xxxx",
"validCode": "xxx",
"brandId":"x",
}) # TODO: 设置全局变量,下面的Step方法都可以使用到。这里设置的Accept、Cookie、userName、password、validCode、brandId
teststeps = [
Step(
RunRequest("登录")
.post(get_base_url()+"API路径") #todo:切换自己的api路径
.with_cookies(**{"aaf875be9aad4feca52ccece8eade2df": "WyIzMTE4NjIwNDQ3Il0"}) # todo:API的cookies
.with_json({"userName": "$userName", "password": "$password", "validCode": "$validCode"}) # todo: 引用定义好的全局变量
.extract()
.with_jmespath('body.data.token', 'token') # todo:从response获取响应值,最外层是固定的body
.with_jmespath('headers.Server', 'cookies') # todo: 后面为变量名 取出 body的data下的token,所以前面指定了body,同理,也可以取header的data(有需要的话)
.validate()
.assert_equal("status_code", 200)
.assert_equal('headers."Content-Type"', "application/json")
.assert_equal("body.code", 200)
.assert_equal("body.message", "success")
),
]
if __name__ == "__main__":
TestCaseLogin().test_start()
写入登录用例,方便后续的hrun文件call,实现login解耦
import time
from httprunner import HttpRunner,Config,Step,RunTestCase,RunRequest
from login.get_token import TestCaseLogin as GetToken # todo:引入login文件
def get_base_url():
from common import config_reader
return config_reader.ConfigReader('../config/base_config.cfg').get_config('backend_url')
class APIRequestConstructor(HttpRunner): # todo:创建基础类,便于后面清除hrun的token信息和读取token信息
config = (
Config("API测试执行")
.base_url(get_base_url())
.verify(False))
teststeps = [
Step(
RunTestCase("通过登录API获取token")
.call(GetToken)
.export(*["token",]))]
def reset_request_step(token=GetToken): # todo:基于类封装方法,默认不传则使用GetToken
APIRequestConstructor.teststeps.clear() # todo:属于单元测试框架内的teardown,给每个用例一个干净的执行环境
APIRequestConstructor.teststeps.append( # todo:添加step动作
Step(
RunTestCase("通过登录API获取token")
.call(token)
.export(*["token",])
))
headers = { # todo:根据项目需要,写入headers数据
"Host": "xxx",
"Accept": "application/json, text/plain, */*",
"Content-Type": "application/json;charset=utf-8",
"brandId": "3",
"Authorization": "${token}",
"Cookie": "xxxx",
}
cookies = {"aaf875be9aad4feca52ccece8eade2df": "WyIxNjUxMTM1OTE1Il0"} # todo:根据项目需要,写入cookies数据
def create_run_request(request_name, url, request_json, body="body.data", headers=headers, cookies=cookies): # todo:封装用例步骤,放置API的名字、请求地址、请求参数
if request_json is not None: # todo:如果请求参数json格式不为空,则执行post请求
return RunRequest(request_name).post(url).with_headers(**headers).with_cookies(**cookies).with_json(
request_json
).extract().with_jmespath(body, "res_data")
else: # todo:如果请求参数json为空,则执行post请求
return RunRequest(request_name).get(url).with_headers(**headers).with_cookies(**cookies) \
.extract().with_jmespath(body, "res_data")
def contruct_request_step(run_request_obj):
return Step(
run_request_obj
)
def perform_requests_and_get_last_response(steps): # todo: 放入用例的执行步骤
APIRequestConstructor.teststeps.extend(steps) # todo:读取teststeps的步骤
obj = APIRequestConstructor() # todo:实例化对象
obj.test_start()
time.sleep(1)
res = obj.with_export(["res_data", "token"]).get_export_variables()
return res
对httprunner的二次封装,减少冗余代码
import copy
import configparser
import os
class ConfigReader:
def __init__(self, file_name):
self.config = configparser.ConfigParser()
config_path = os.path.split(os.path.realpath(__file__))[0] + os.sep + file_name
self.config.read(config_path, "utf-8")
def get_config(self, config_item):
sections = self.config.sections()
for section in sections:
if self.config.has_option(section, config_item):
return self.config.get(section, config_item)
class DB():
def __init__(self, database_name):
cf = ConfigReader("database_config_dev.cfg") # todo:连接的哪个数据库,如果有多套环境,记得这里也要切换下数据库配置
self.host = cf.get_config(database_name + "_host")
self.port = cf.get_config(database_name + "_port")
self.user = cf.get_config(database_name + "_user")
self.password = cf.get_config(database_name + "_password")
self.database_name = database_name
def connect(self): # todo:创建连接数据库方法
import pymysql
# 创建数据库连接
self.db = pymysql.connect(host=self.host, user=self.user, password=
self.password, database=self.database_name)
# 创建游标
self.cursor = self.db.cursor()
def get_one(self, sql): # todo:返回一条数据
result = 0
try:
self.connect()
self.cursor.execute(sql)
result = self.cursor.fetchone()
self.close()
except Exception as e:
print('select error', e)
return result
def get_all(self, sql): # todo:返回全部符合条件的查询结果
result = 0
try:
self.connect()
self.cursor.execute(sql)
result = self.cursor.fetchall()
self.close()
except Exception as e:
print("select error", e)
return result
def __edit(self, sql): # 创建主函数
result = 1 # 设置结果集,用于调用的时候做判断
try: # 这里是使用的try语句来尝试进行操作
self.connect()
self.cursor.execute(sql)
self.db.commit() # 注意的是,如果是对数据库做了修改、删除、增加的操作,那么一定要commit提交,查询和创建表不需要提交
self.close()
except Exception as e: # 如果操作失败,报出操作异常,且游标进行回滚
print('error :', e)
result = 0
self.db.rollback()
return result
def insert(self, sql):
# 插入语句 ,以下三个都是一样的,只是调用的时候,我们看起来更加清晰而已
return self.__edit(sql) # 通过主函数的处理,来去执行sql语句
def delete(self, sql): # 删除语句
return self.__edit(sql)
def update(self, sql): # 修改语句
return self.__edit(sql)
def close(self): # 关闭方法
self.cursor.close() # 关闭游标
self.db.close() # 关闭数据库
[PMSClaimH3H51]
PMSClaimH3H51_host=xxx
PMSClaimH3H51_port=xxx
PMSClaimH3H51_user=xxx
PMSClaimH3H51_password=xxxx
[PMSCommonH3H5]
PMSCommonH3H5_host=xxx
PMSCommonH3H5_port=xxxx
PMSCommonH3H5_user=xxx
PMSCommonH3H5_password=xx
import allure,pytest
from loguru import logger
from api_util.util import *
@allure.feature("配件信息管理查询")
class TestSearChParts():
@pytest.fixture(scope="class", autouse=True)
@allure.feature("数据清除")
def teardown_(self):
yield
logger.debug("Implementation of the use case will end.")
@allure.story("分公司:配件信息管理查询")
def test_Search_Parts_GetToken(self):
reset_request_step() # todo: 登录文件引用,默认不传是GetToken
# todo: 创建请求方法 create_search_parts 可自定义命名。调用create_run_request方法,传入API名称、API地址、request body
create_search_parts = \
create_run_request(
"配件信息管理", "api/common/partsInfo/sparepart/v1/getSparePartByPage", {"status":1,"pageSize":10,"pageNum":1,"loading":"false"}) \
.with_jmespath("body.data.list[0].id","ids") \
.validate() \
.assert_equal("status_code", 200).assert_equal("body.message", "success")
# todo: contruct_request_step 放入执行步骤,如果有多个,需要逗号隔开
create_out_of_warranty_payment_step = contruct_request_step(
create_search_parts)
# todo: perform_requests_and_get_last_response 放入需要执行用例,同样,如果有多个,需要逗号隔开
res = perform_requests_and_get_last_response(
[create_out_of_warranty_payment_step])
id = res['res_data']['list'][0]['id'] # todo: 取出接口response响应值,拿到依赖接口使用。因为封装的方法,response的body外部是res_data,所以这里需要加上
print(id)
@allure.story("服务站:配件信息管理查询")
def test_Search_Parts_Service_Station(self):
reset_request_step(Service_Station)
create_out_of_warranty_payment_run_request = \
create_run_request(
"配件信息管理", "api/common/partsInfo/sparepart/v1/getSparePartByPage",
{"status": 1, "pageSize": 10, "pageNum": 1, "loading": "false"}) \
.validate() \
.assert_equal("status_code", 200).assert_equal("body.message", "success")
create_out_of_warranty_payment_step = contruct_request_step(
create_out_of_warranty_payment_run_request)
res = perform_requests_and_get_last_response(
[create_out_of_warranty_payment_step])
pytest [filename]
也可以使用 hrun [filename]
如果要运行整个文件集下的所有用例,可以直接pytest [dirname]
pytest xxxx --alluredir ./report # todo: xxx为hrun文件名字,也可以是文件夹名字。生成allure结果集
allure serve report # todo:本地查看allure报告(控制台会给出url地址)
allure generate ./report/ -o ./reports --clean # todo:清除历史allure结果集,在reports文件夹下新增allure报告
Jenkins系统管理——全局工具配置——Allure Commandline添加配置
勾选自动安装,选择from maven central,版本选择最新版本,目前是2.7
配置git
Repository URL : git地址,记得后面加上.git
Credentials : 拉取git代码凭证
点击【添加】按钮,进入jenkins添加凭证弹窗内
**Branches to build:**分支填写 */master,master分支,如果有需要,可以填写其它分支
cron定时配置
点问号,看帮助即可。这里设置的是每天凌晨6点构建一次job
shell构建
这里选择Execute shell,使用shell命令执行。这里的因为Jenkins拉取代码会在固定的.jenkins/workspace这个目录,${WORKSPACE}变量指向了这个地址。
最后保存就可以了!
Post-build Actions:构建后动作,点击add post-build action选项,选择allure,Results的path中填写的名称要和${WORKSPACE}后的名称,不然报告内容为空。
在首页可以查看新的任务,点击最右方小图标就可以构建了。也可以点击name进入任务中,点击开始构建。点击Allure Report就可以查看本次运行的测试结果。
https://blog.csdn.net/weixin_42923777/article/details/102551493