BATJ 测试大佬藏着掖着的神技 -HttpRunner

发布于 更新于
本文目录
  1. 一、思考
  2. 1.自动化测试要做哪些事?
  3. 2.HttpRunner是什么?
  4. 二、HttpRunner
  5. 1.安装
  6. 2.创建项目目录
  7. 3.测试准备
  8. 4.初体验
  9. 5.优化用例
  10. 6.总结

技术文章自动化测试

需求分析-->测试计划-->测试方案

一、思考

image.png

1.自动化测试要做哪些事?

  • 需求分析-->测试计划-->测试方案

  • 编写测试用例

  • 数据驱动

    • ddt

  • 测试数据管理

    • excel

    • csv

    • 数据库(MySQL、MongoDB等)

  • 配置信息管理

    • 配置文件

  • 日志记录与分析

    • 日志器

  • unittest

    • 断言结果比对

  • Jenkins持续集成


2.HttpRunner是什么?

简洁

  • HttpRunner 是一个适应HTTP、HTTPS协议的强大测试框架,基于Python开发的

  • 往往测试人员只需编写一份 YAML或者JSON格式的脚本,用于存放测试用例或者测试数据

  • 可以非常方便、非常高效地实现接口自动化测试、性能测试、Jenkins持续集成等多种测试需求

设计理念

image.png
  • 本身并没有做大的创新,而是将各大优秀的开源项目进行整合

  • 完全利用Python中强大的Requests请求库、充分结合pytest测试框架以及Locust框架

  • 利用内置的功能模块,支持将Fiddler、Charles抓包软件导出的HAR 格式文件转化为YAML或者JSON格式的测试用例文件

  • 支持在YAML或者JSON格式的测试用例文件中调用Python函数,来动态获取参数或者实现数据库校验

  • 支持命令行运行用例,结合Jenkins非常便捷的实现持续集成

  • 自带日志记录功能,可自定义日志等级和日志保存的文件夹

  • 拓展性极其强大,轻轻松松实现二次开发和自动化测试平台化开发


二、HttpRunner

1.安装

pip install httprunner


2.创建项目目录

  • 快速创建项目目录结构

    hrun --startproject api_test


3.测试准备

image.pngimage.png
  • 使用Fiddle抓包,将抓取得到的数据包导出为 HAR 格式的文件

  • 生成测试用例

    har2case data_sources/register.har -2y


4.初体验

image.png

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.优化用例

image.png

安装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.总结

image.png
  • HttpRunner功能及其强大,功能远不止上述展示的内容

  • 还能进行数据驱动、参数化、接口依赖、CSV文件数据管理、二次开发等

  • 后续可能会出一套教程专门讲解HTTPRunner,如何将其应用于实际工作中?如何打造自动化测试平台?性能自动化如何开展?等等,敬请期待!


来源:testingpai

继续阅读