BATJ 测试大佬藏着掖着的神技 -HttpRunner
需求分析-->测试计划-->测试方案
一、思考

1.自动化测试要做哪些事?
需求分析-->测试计划-->测试方案
编写测试用例
数据驱动
ddt
测试数据管理
excel
csv
数据库(MySQL、MongoDB等)
配置信息管理
配置文件
日志记录与分析
日志器
unittest
断言结果比对
Jenkins持续集成
2.HttpRunner是什么?
简洁
HttpRunner 是一个适应HTTP、HTTPS协议的强大测试框架,基于Python开发的
往往测试人员只需编写一份 YAML或者JSON格式的脚本,用于存放测试用例或者测试数据
可以非常方便、非常高效地实现接口自动化测试、性能测试、Jenkins持续集成等多种测试需求
设计理念

本身并没有做大的创新,而是将各大优秀的开源项目进行整合
完全利用Python中强大的Requests请求库、充分结合pytest测试框架以及Locust框架
利用内置的功能模块,支持将Fiddler、Charles抓包软件导出的HAR 格式文件转化为YAML或者JSON格式的测试用例文件
支持在YAML或者JSON格式的测试用例文件中调用Python函数,来动态获取参数或者实现数据库校验
支持命令行运行用例,结合Jenkins非常便捷的实现持续集成
自带日志记录功能,可自定义日志等级和日志保存的文件夹
拓展性极其强大,轻轻松松实现二次开发和自动化测试平台化开发
二、HttpRunner
1.安装
pip install httprunner
2.创建项目目录
快速创建项目目录结构
hrun --startproject api_test
3.测试准备


使用Fiddle抓包,将抓取得到的数据包导出为 HAR 格式的文件
生成测试用例
har2case data_sources/register.har -2y
4.初体验

testcases/register.yaml
- config:
name: testcase description
variables: {}
- test:
name: /users/register/
request:
headers:
Content-Type: application/json
User-Agent: PostmanRuntime/7.15.0
json:
mobile: '18044441116'
password: '123456'
password_repeat: '123456'
username: python6
method: POST
url: http://127.0.0.1:8088/users/register/
validate:
- eq:
- status_code
- 200
- eq:
- headers.Content-Type
- application/json
- eq:
- content.errno
- '0'
- eq:
- content.errmsg
- 恭喜您,注册成功!
- eq:
- content.data
- null
5.优化用例

安装fake-useragent第三方模块,随机生成user-agent
pip install fake-useragent
在debugtalk.py文件中,添加随机生成user-agent的函数
from fake_useragent import UserAgent
def get_user_agent():
"""
获取随机user agent
:return:
"""
return UserAgent().random
在debugtalk.py中创建生成未注册手机号、未注册用户名以及随机密码的函数
import random
import string
from scripts.handle_mysql import HandleMysql
def get_not_register_tel():
"""
获取一个未注册的手机号
:return:
"""
do_mysql = HandleMysql()
tel = do_mysql.create_not_existed_moblie()
do_mysql.close()
return tel
def get_not_register_username():
"""
获取一个未注册的用户名
:return:
"""
do_mysql = HandleMysql()
username = do_mysql.create_not_existed_username()
do_mysql.close()
return username
def get_random_password(count):
"""
生成密码
:param count: 密码位数
:return:
"""
passwd_list = [random.choice(string.digits + string.ascii_letters) for _ in range(count)]
return ''.join(passwd_list)
优化后的yaml格式的用例文件
- config:
name: "注册接口测试"
variables: {}
base_url: http://127.0.0.1:8088
- test:
name: /users/register/
variables:
password: ${get_random_password(5)}
request:
headers:
Content-Type: application/json
User-Agent: ${get_user_agent()}
json:
mobile: ${get_not_register_tel()}
password: $password
password_repeat: $password
username: ${get_not_register_username()}
method: POST
url: /users/register/
validate:
- eq: [status_code, 200]
- eq: [headers.Content-Type, application/json]
- eq: [content.errno, '0']
- eq: [content.errmsg, '恭喜您,注册成功!']
- eq: [content.data, null]
在testcases/register.yaml用例文件中,可以调用debugtalk.py中创建的get_user_agent、get_not_register_tel、get_not_register_username、get_random_password函数
需要使用${函数名(参数)}来调用
可以在全局config配置中,定义base_url来指定每个test用例的url前缀
YAML文件中,一个test就是一条用例信息
variable下面可以设置用例的变量
request为请求相关的参数,headers下面为请求头信息
json下面为以json格式向服务器发起请求的参数
method为请求方法
url为请求的url地址,完整地址会与base_url进行拼接
validate下面为,断言信息
使用pymysql模块,对mysql执行SQL语句,将相关操作进行封装,封装后的代码,如下所示:
handle_mysql.py
import random
import string
import pymysql
from scripts.handle_config import do_config
class HandleMysql:
"""
处理mysql
"""
def __init__(self):
self.conn = pymysql.connect(host=do_config("mysql", "host"),
user=do_config("mysql", "user"),
password=do_config("mysql", "password"),
db=do_config("mysql", "db"),
port=do_config("mysql", "port"),
charset=do_config("mysql", "charset"),
cursorclass=pymysql.cursors.DictCursor
)
self.cursor = self.conn.cursor()
@staticmethod
def create_mobile():
"""
随机生成11位手机号
:return: 返回一个手机号字符串
"""
start_mobile = ['130', '131', '132', '133', '134', '135', '136', '137', '138', '139',
'150', '151', '152', '153', '155', '156', '157', '158', '159',
'180', '181', '182', '183', '184', '185', '186', '187', '188', '189']
start_num = random.choice(start_mobile)
end_num = ''.join(random.sample(string.digits, 8))
return start_num + end_num
def create_username(self):
"""
创建用户名
:return: 返回用户名字符串
"""
sql = "SELECT username FROM `tb_users` ORDER BY username DESC LIMIT 0, 1;"
username_src = self(sql=sql)['username']
one_list = list(username_src)
random.shuffle(one_list)
return ''.join(one_list)
def is_existed_mobile(self, mobile):
"""
判断给定的手机号在数据库中是否存在
:param mobile: 11位手机号组成的字符串
:return: True or False
"""
sql = "SELECT mobile FROM `tb_users` WHERE mobile=%s;"
if self(sql, arg=(mobile, )): # 手机号已经存在,则返回True,否则返回False
return True
else:
return False
def is_existed_username(self, username):
"""
判断给定的用户名在数据库中是否存在
:param username: 用户名
:return: True or False
"""
sql = "SELECT username FROM `tb_users` WHERE username=%s;"
if self(sql, arg=(username, )): # 用户名已经存在,则返回True,否则返回False
return True
else:
return False
def create_not_existed_moblie(self):
"""
随机生成一个在数据库中不存在的手机号
:return: 返回一个手机号字符串
"""
while True:
one_mobile = self.create_mobile()
if not self.is_existed_mobile(one_mobile):
break
return one_mobile
def create_not_existed_username(self):
"""
随机生成一个在数据库中不存在的用户名
:return: 返回一个用户名字符串
"""
while True:
one_username = self.create_username()
if not self.is_existed_username(one_username):
break
return one_username
def get_existed_moblie(self):
"""
获取一个在数据库中已经存在的手机号
:return: 返回一个手机号字符串
"""
sql = "SELECT mobile FROM `tb_users` LIMIT 0, 1;"
one_mobile = self(sql) # 获取数据库中第一个手机号
return one_mobile["mobile"]
def get_existed_username(self):
"""
获取一个在数据库中已经存在的用户名
:return: 返回一个用户名字符串
"""
sql = "SELECT username FROM `tb_users` LIMIT 0, 1;"
one_username = self(sql) # 获取数据库中第一个用户名
return one_username["username"]
def __call__(self, sql, arg=None, is_more=False):
"""
:param sql: 传入的sql语句,组成的字符串
:param is_more: 是否获取多个值,默认获取单条数据
:param arg: 传给sql的额外参数
:return:
"""
self.cursor.execute(sql, arg)
self.conn.commit()
if is_more:
result = self.cursor.fetchall()
else:
result = self.cursor.fetchone()
return result
def close(self):
self.cursor.close()
self.conn.close()
if __name__ == '__main__':
do_mysql = HandleMysql()
print(do_mysql.create_not_existed_moblie())
print(do_mysql.get_existed_moblie())
print(do_mysql.create_not_existed_username())
print(do_mysql.get_existed_username())
do_mysql.close()
6.总结

HttpRunner功能及其强大,功能远不止上述展示的内容
还能进行数据驱动、参数化、接口依赖、CSV文件数据管理、二次开发等
后续可能会出一套教程专门讲解HTTPRunner,如何将其应用于实际工作中?如何打造自动化测试平台?性能自动化如何开展?等等,敬请期待!
来源:testingpai