一、思考
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.com!
注册 关于