Python自动化 BATJ 测试大佬藏着掖着的神技-HttpRunner

kouyou · 2019年06月28日 · 最后由 huangchen1019 回复于 2020年04月12日 · 1246 次阅读

一、思考

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,如何将其应用于实际工作中?如何打造自动化测试平台?性能自动化如何开展?等等,敬请期待!


共收到 117 条回复

keyou大佬棒棒哒!

可优大佬最棒!我的作业完成了!赞也点过了!(此条5毛,括号内删除)

3楼 已删除

优秀哦,乡亲们!!!

村长666,这是自动化评论

没有灵魂的机器人说:可优真帅!

来交作业了~~~~

狮虎猎物获威名,可怜麋鹿有谁怜?

世间从来强食弱,纵使有理也枉然。

可优大佬讲的东西很有价值!点个赞!

挟飞仙以遨游,抱明月而长终。

厉害了

拔草来了

老师授业解惑,醍醐灌顶

有匪君子,如切如磋,如琢如磨

交作业

可优大佬棒棒的!

可优村长老厉害了!!!!!!!,(作业自动化调试)58370

keyou is a smart man!

keyou is a smart man!!51

可优大佬棒棒的!20190815184751

可优大佬棒棒的!20190815185130

可优大佬bang bang bang!!!

20190815230324:可优大佬,python自动化测试坑好多,如何避免踩坑?

20190815231818:可优大佬,喜欢你课上的图片,既励志又不鸡汤

20190815232230:可优大佬,如何将python学好呢?

20190815232523:可优大佬,初听你的课还不习惯,现在越来越习惯,戒不掉

20190815233406:可优大佬,不上测开还能问问题?

20190815233816:老铁,来深圳呀,哈哈!

20190815234300:可优大佬,还是想说,教完自动化再2撤呀

20190815234703:可优大佬,python自动化测试坑好多,如何避免踩坑?

20190815235104:可优大佬,初听你的课还不习惯,现在越来越习惯,戒不掉

20190816001621:可优大佬,还想听你的课

20190816004825:可优大佬,喜欢你课上的图片,既励志又不鸡汤

文章通俗易懂,内容很有用

写的真好!

可优大佬课讲得很棒,再多多练习英文发音就更完美啦!!82

可优大佬棒棒哒

厉害厉害

受益匪浅

干货满满

可优大佬棒棒的,大赞

可优大佬棒棒的,大赞赞

very good

46楼 已删除

keyou is a smart man!!94

村长,干货满满!+93640赞!

keyou is a smart man!!30

keyou is a smart man!!44

keyou is a smart man!!20

可优老师明明可以靠才华,偏偏要靠颜值

2019-08-18 01:02:31可优老师明明可以靠才华,偏偏要靠颜值

老徐拜读BATJ 测试大佬藏着掖着的神技-HttpRunner

老徐2019-08-18 19:50:19拜读:BATJ 测试大佬藏着掖着的神技-HttpRunner

老徐2019-08-18 19:55:28拜读:BATJ 测试大佬藏着掖着的神技-HttpRunner

可优男神,真厉害啊!什么都会

可优老师,棒棒哒!都是干货!

2019-08-19 17:16:53可优老师明明可以靠才华,偏偏要靠颜值

可有村长起来除草啦

可优大佬的声音还有磁性。。。20190820003930like.png

20190820011458:老铁,来深圳呀,哈哈!

可优大佬写得真棒!赞赞赞!!!

老师授业解惑,学生醍醐灌顶

大佬666_20190820105134

可优大佬超厉害的!

可优大佬宇宙第一帅!

可优大佬棒棒哒!

浅显易懂,受教啦!

好好学习学习!

厉害厉害哈!

我是可优大佬小迷弟!!

可优大佬超厉害的!

我是可优大佬小迷妹!

可优大佬宇宙第一帅!!

可优大佬棒棒哒!! 浅显易懂,受教啦!!

可优男神~~ 浅显易懂,受教啦!!好好学习学习!!

给可优大佬点赞!!_2019-08-20 19:53:07.302635

可优真棒

可优大佬讲课棒棒哒!

可优村长老厉害了!!!!!!!,(作业自动化调试)13960

可优村长老厉害了!!!!!!!,(作业自动化调试)39561

可优村长老厉害了!!!!!!!,(作业自动化调试)04357

大佬棒棒的!20190822223426

可优大佬棒棒的!20190822224420

可优大佬,可优大佬!

给可优大佬点赞!!_2019-08-23 17:37:25.909104

2019-08-24 14:25:38可优老师明明可以靠才华,偏偏要靠颜值

干货满满,收益匪浅!2019-08-25 22:32:30

干货满满,收益匪浅!2019-08-25 23:04:42

生活因你而火热

大王叫我来巡山!!我到这儿转一转!!!

大王叫我来巡山!!!我到这儿转一转!!!

点赞可优大佬!666

赞,通俗易懂,已收藏

keyou大佬威武霸气!

2019-08-27 18:13:31可优老师我比你帅多了

受益匪浅

2019-09-09 12:14:46可优老师明明可以靠才华,偏偏要靠颜值

20190920235619:每一发奋努力的背后,必有加倍的赏赐。

20190921122826:可优大佬,你走了,没人督促拔草了,田里长满了草

20190921172033:可优大佬,不上测开还能问问题?

20190921174358:最喜欢上可优大佬的课

可优老师是集颜值与才华于一身的大佬

可优大佬明明可以靠颜值挣钱,非要靠实力

可优大佬在课间休息时间最爱播放的歌曲是:Adele唱的,我也爱听

可优大佬讲课诙谐幽默,易于理解

可优老师得帖子很有价值!!都是干货!!

干货!!真棒。

喜欢可优老师讲课,喜欢看可优老师的帖子。

666文章浅显易懂,手动点赞

满满干货,好好学习!!!

满满干货,好好学习,天天向上!!!

手动点赞!!

好赞

手动给可优老师点赞!

这是自动化执行发布的评论!2020_04_12_15_35_06

这是自动化执行发布的评论!2020_04_12_15_37_37

这是自动化执行发布的评论!2020_04_12_15_39_29

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册