Httprunner系列(十):企业级接口自动化测试框架定制


前言

Httprunner作者是debugtalk,是一个资深的开发者,目前开发httprunner已有5个版本
Httprunner用法比较简单,并且符合工作中的多种场景:
1、做完接口自动化,要求继续做此接口的性能测试(hrun内置locust模块,可以直接做压测)
2、需要做的接口过多,而且只需要替换部分的参数,接口自动化的工作就会完成(charles录制并导出.har文件,通过hrun命令转成.py文件)
3、request库的每一步的接口响应,都需要加断言,而hrun会自动生成断言方法
4、hrun集成的第三方库:loguru、jmespath、pytest


一、hrun目前的版本

在这里插入图片描述

二、环境准备 & httprunner的基本操作

1.安装python、Httprunner、allure-pytest:

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple httprunner
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple allure-pytest

2.环境验证,查看httprunner版本:

httprunner -V   

在这里插入图片描述

3.查看httprunner帮助:

httprunner --help  

在这里插入图片描述

4.生成脚手架(快速的生成一个hrun框架):

httprunner startproject  PartsService

在这里插入图片描述
在这里插入图片描述

5.脚手架内的yml文件转换pytest文件(根目录下执行 ):

 cd  PartsService
 hmake testcases

在这里插入图片描述

6.运行httprunner,单个文件(进入要运行文件的根目录下)

方式一:		pytest [filename]
方式二:		hrun [filename]
方式三:		httprunner run  [filename]

7.批量运行httprunner,运行文件集下的所有用例

方式一:				pytest [dirname]
方式二:				hrun [dirname]
方式三:				 httprunner run  [dirname]

8. 查看.har生成脚本帮助

har2case -h  

三、httprunner的二次封装

在这里插入图片描述

1. 框架内的各文件解释

allure:存放allure的报告结果集
api_data:存放接口的请求数据(可以是json文件、xlsx文件、yaml文件)
api_testing:存放api测试用例
api_util:便于api测试的封装方法
common:公共方法,比如读取数据库文件
config:存放全局配置文件
databases:存放数据库文件
login:登录解耦

2. config文件内容

在这里插入图片描述

[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来进行切换

3. common文件内容

在这里插入图片描述

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文件方法,便于切换多套数据库环境,后续有需要可以做补充

4.login文件内容

在这里插入图片描述

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解耦

5.api_util文件内容

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的二次封装,减少冗余代码

6.databases文件内容

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()  # 关闭数据库

7.databases_config_dev.cfg文件内容

在这里插入图片描述

[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

在这里插入图片描述
像是这里,有两套数据库环境,用这个方法封装就比较好用

8.api_testing文件内容

在这里插入图片描述

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])

四、运行文件方法

1.右击文件来运行

在这里插入图片描述

2.点击方法左侧绿三角来执行单条用例

在这里插入图片描述

3.使用命令运行单个脚本

在这里插入图片描述
pytest [filename]
在这里插入图片描述
也可以使用 hrun [filename]

在这里插入图片描述

4.使用命令运行某个文件集下的所有用例

如果要运行整个文件集下的所有用例,可以直接pytest [dirname]

五、本地生成allure报告

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配置pytest+httprunner

1.在jenkins上安装allure插件

Jenkins系统管理——全局工具配置——Allure Commandline添加配置
勾选自动安装,选择from maven central,版本选择最新版本,目前是2.7
在这里插入图片描述

2.配置job

新建任务:自由风格的软件项目
在这里插入图片描述

3.进入job配置

在这里插入图片描述
配置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}后的名称,不然报告内容为空。

在这里插入图片描述

在这里插入图片描述

4.运行Jenkins

在首页可以查看新的任务,点击最右方小图标就可以构建了。也可以点击name进入任务中,点击开始构建。点击Allure Report就可以查看本次运行的测试结果。
在这里插入图片描述
在这里插入图片描述

5.详细的jenkins配置(转)

https://blog.csdn.net/weixin_42923777/article/details/102551493

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值