接口自动化pytest+request+allure+logging+yaml+ddddocr验证码识别

1、封装工具类common

fixture夹具

import pytest
from common.yaml_util import YamlUtil


@pytest.fixture(scope='session',autouse=True)
def excute_sql():
    #print("连接数据库")
    YamlUtil().clean_yaml()#清空extract.yaml文件中的token、userid
    yield #print("测试执行什么")#作用是控制执行用例中执行什么
    # print("关闭数据库")

图像识别

(demo中暂时未使用,实际测试效果差强人意)此处加了一些自己图片处理逻辑,可以按照实际情况来修改逻辑;pyocr效果没有这个好

import os
import time
from PIL import Image
import ddddocr
import requests

def returncode():
    re1=requests.request(method='get',url='http://localhost:8081/api/checkCode')
    # print(re1)
    with open(os.getcwd()+'\images\ceshi1.jpg',mode='wb') as f:
        f.write(re1.content)
    # 打开图片文件
    image = Image.open(os.getcwd()+"\images\ceshi1.jpg")

    # 将图像转换为灰度图像
    # gray_image = ImageOps.grayscale(image)
    # gray_image.show()
    # 转换为黑白二值图像,调整阈值以减少噪声和干扰,
    # 直接降噪,不灰度处理
    binary_image = image.point(lambda x: x < 200 and 255)
    binary_image.save(os.getcwd()+'\images\ceshi2.jpg')
    time.sleep(3)
    # 显示处理后的图像
    # binary_image.show()
    with open(os.getcwd()+'images\ceshi2.jpg','rb') as f:
        img=f.read()
    ocr=ddddocr.DdddOcr()
    result=ocr.classification(img)
    return result

封装请求、日志、接口关联数据操作、断言

需要按照实际场景进行微小修改

import logging

import jsonpath
import requests
import json
from common.yaml_util import YamlUtil
from builtins import str
import re


class RequestUtil:

    def __init__(self, two_node):
        self.base_url = YamlUtil().read_config('base', two_node)

    # 替换值的方法
    # #(替换url,params,data,json,headers)
    # #(string,int,float,list,dict)
    def replace_value(self, data):
        if data:
            # 保存数据类型
            data_type = type(data)
            # 判断数据类型转换成str
            if isinstance(data, dict) or isinstance(data, list):
                str_data = json.dumps(data)
            else:
                str_data = str(data)
            for cs in range(1, str_data.count('${') + 1):
                # 替换
                if "${" in str_data and "}" in str_data:
                    start_index = str_data.index("${")
                    end_index = str_data.index("}", start_index)
                    old_value = str_data[start_index:end_index + 1]
                    new_value = YamlUtil().red_yaml(old_value[2:-1])
                    str_data = str_data.replace(old_value, str(new_value))
            # 还原数据类型
            if isinstance(data, dict) or isinstance(data, list):
                data = json.loads(str_data)
            else:
                data = data_type(str_data)
        return data

    # 规范yaml测试用例
    def standard_yaml(self, caseinfo):
        caseinfo_keys = caseinfo.keys()
        # 判断一级关键字是否包含:name,request,validate
        if "name" in caseinfo_keys and "request" in caseinfo_keys and "validate" in caseinfo_keys:
            # 判断request下面是否包含:method、url
            request_keys = caseinfo["request"].keys()
            if "method" in request_keys and "url" in request_keys:
                print("yaml基本架构检查通过")
                logging.debug(msg="{}请求信息:{}".format(caseinfo["title"],caseinfo["request"]))
                method = caseinfo['request'].pop("method")  # pop() 函数用于移除列表中的一个元素,并且返回该元素的值。
                url = caseinfo['request'].pop("url")
                res = self.send_request(method, url, **caseinfo['request'])  # caseinfo需要解包加**
                return_text = res.text
                logging.debug(msg="{}响应信息:".format(caseinfo["title"]) + res.text)
                return_code = res.status_code
                return_json = ""
                try:
                    return_json = res.json()
                except Exception as e:
                    print("extract返回的结果不是JSON格式")
                # 提取值并写入extract.yaml文件
                if "extract" in caseinfo.keys():
                    for key, value in caseinfo["extract"].items():
                        if "(.*?)" in value or "(.+?)" in value:  # 正则表达式
                            zz_value = re.search(value, return_text)
                            if zz_value:
                                extract_value = {key: zz_value.group(1)}
                                YamlUtil().write_yaml(extract_value)
                        else:  # jsonpath
                                resturn_json = res.json()
                                js_value = jsonpath.jsonpath(resturn_json, value)
                                if js_value:
                                    extract_value = {key: js_value[0]}
                                    YamlUtil().write_yaml(extract_value)
                # 断言:
                logging.debug(msg="{}断言信息:{}".format(caseinfo["title"],caseinfo["validate"]))
                self.assert_result(caseinfo['validate'], return_json, return_code)
                return res

            else:
                print("在request下必须包含method,url")
        else:
            print("一级关键字必须包含name,request,validate")

    sess = requests.session()

    # 统一请求封装

    def send_request(self, method, url, **kwargs):
        method = str(method).lower()  # 转换小写
        # 基础路径的拼接和替换
        url = self.base_url + self.replace_value(url)
        print(url)
        # 参数替换
        for key, value in kwargs.items():
            if key in ['params', 'data', 'json', 'headers','url']:
                kwargs[key] = self.replace_value(value)
            elif key == "files":
                for file_key, file_path in value.items():
                    value[file_key] = open(file_path, 'rb')
        res = RequestUtil.sess.request(method, url, **kwargs)
        return res

        # 断言

    def assert_result(self, yq_result, sj_result, return_code):
        all_flag = 0
        for yq in yq_result:
            for key, value in yq.items():
                print(key, value)
                if key == "equals":
                    flag = self.equals_assert(value, return_code, sj_result)
                    all_flag = all_flag + flag
                elif key == 'contains':
                    flag = self.contains_assert(value, sj_result)
                    all_flag = all_flag + flag
                else:
                    print("框架暂不支持此段断言方式")
        assert all_flag == 0 , logging.debug(msg="Failed")

    # 相等断言
    def equals_assert(self, value, return_code, sj_result):
        flag = 0
        for assert_key, assert_value in value.items():
            print(assert_key, assert_value)
            if assert_key == "status_code":  # 状态断言
                if assert_value != return_code:
                    flag = flag + 1
                    print("断言失败,返回的状态码不等于%s" % assert_value)
            else:
                lists = jsonpath.jsonpath(sj_result, '$..%s' % assert_key)
                if lists:
                    if assert_value not in lists:
                        flag = flag + 1
                        print("断言失败:" + assert_key + "不等于" + str(assert_value))
                else:
                    flag = flag + 1
                    print("断言失败:返回的结果不存在:" + assert_key)
        return flag

    # 包含断言
    def contains_assert(self, value, sj_result):
        flag = 0
        if value not in str(sj_result):
            flag = flag + 1
            print("断言失败:返回的结果中不包含:" + value)
        return flag

封装yaml文件操作方法

extract.yaml文件是存在接口关联的数据,提取的数据

import os

import yaml

class YamlUtil:
# 读
    def red_yaml(self,key):
        with open(os.getcwd() + '/extract.yaml', mode='r', encoding='utf-8') as f:
            value = yaml.load(f, yaml.FullLoader)
            return value[key]
        # 写


    def write_yaml(self,data):
        with open(os.getcwd() + '/extract.yaml', mode='a+', encoding='utf-8') as f:
            yaml.dump(data, stream=f, allow_unicode=True)

        # 清空


    def clean_yaml(self):
        with open(os.getcwd() + '/extract.yaml', mode='w', encoding='utf-8') as f:
            f.truncate()
        # 读yaml文件


    def read_yamlfile(slef,file):
        with open(os.getcwd() + '/' + file, mode='r', encoding='utf-8') as f:
            value = yaml.load(f, yaml.FullLoader)
            return value

    def read_config(self,one_node,two_node):
        with open(os.getcwd() + '/config.yaml', mode='r', encoding='utf-8') as f:
            value = yaml.load(f, yaml.FullLoader)
            return value[one_node][two_node]

2、配置文件

编辑环境控制yaml文件

base:
  base_test_url: http://test
  base_normal_url: https://***

编辑pytest.ini文件

[pytest]
log_cli = true
log_cli_level = DEBUG
log_cli_format = %(asctime)s %(levelname)s %(message)s %(filename)s
log_file = ./log/test.log
log_file_level = DEBUG
log_date_format = %Y-%m-%d %H:%M:%S
log_file_format = %(asctime)s %(levelname)s %(message)s %(filename)s
log_file_date_format = %Y-%m-%d %H:%M:%S

;addopts = -sv -m "smoke" --reruns 2 -n 2 --html=./reports/report3.html --capture=sys
addopts = -sv --alluredir=./tmps --clean-alluredir
testpaths = ./testcases
python_files = test_*.py
python_classes = Test_*
python_functions = test_*
markers=
    smoke:冒烟测试

3、编写测试用例以及执行文件

编写测试用例

import allure
from common.conftest import *
from common.yaml_util import YamlUtil
from common.request_url import RequestUtil


class Test_ap:

    @allure.feature("登录")
    @allure.story("登录接口")
    @pytest.mark.smoke
    @pytest.mark.parametrize("caseinfo",YamlUtil().read_yamlfile("testcases/test_login.yaml"))
    def test_login(self,excute_sql,caseinfo):
        print(excute_sql)
        # Yamlutil().write_yaml(excute_sql)
        res = RequestUtil("base_test_url").standard_yaml(caseinfo)

    @allure.feature("用户管理")
    @allure.story("用户管理接口")
    @pytest.mark.smoke
    @pytest.mark.parametrize("caseinfo",YamlUtil().read_yamlfile("testcases/test_curd_user.yaml"))
    def test_adduser(self,excute_sql,caseinfo):
        print(excute_sql)
        # Yamlutil().write_yaml(excute_sql)
        res = RequestUtil("base_test_url").standard_yaml(caseinfo)

编写执行文件

import os
import time

import pytest


if __name__ == '__main__':
    pytest.main(['-sv','--html=./reports/report.html'])
    time.sleep(4)
    os.system("allure generate ./tmps -o ./reportsallure --clean")

编写登录yaml测试数据;存在提取token

-
  featrue: 登录
  story: 登录接口
  title: 不存在账户登录
  name: 1
  request:
    method: post
    url: /api/auth/oauth/token
    data:
      username: admin1
      password: e85ce909a38157c2fc
      grant_type: password
      client_id: hmc
      client_secret: 123456
  validate:
    - equals: {status_code: 200}
    - contains: "用户名不存在"
-
  featrue: 登录
  story: 登录接口
  title: 正常登录
  name: 1
  request:
    method: post
    url: /api/auth/oauth/token
    data:
      username: admin
      password: e85ce909a38157c2fcb022
      grant_type: password
      client_id: hmc
      client_secret: 123456
  extract:
    access_token: $.data.access_token
  validate:
    - equals: {status_code: 200}
    - contains: "OK"

编写用户增删改查(curd)测试用例数据。存在提取userid此处需要注意yaml文件的编写方式

-
  name: 1
  feature: 管理用户
  story: 新增用户
  title: 新增用户接口
  request:
    url: /api/system/user/add
    method: post
    json: {"deptId":"12","userName":"testsa","nickName":"test","roleId":3,"deptType":0}
    headers:
      "Content-Type": "application/json"
      "Authorization": "Bearer ${access_token}"
  validate:
    - equals: {status_code: 200}
    - contains: "OK"

-
  name: 1
  feature: 管理用户
  story: 查询用户
  title: 查询用户接口
  request:
    url: /api/system/user/loadList
    method: post
    json: {"pageNum":1,"pageSize":10,"username":"testsa"}
    headers:
      "Content-Type": "application/json"
      "Authorization": "Bearer ${access_token}"
  validate:
    - equals: {status_code: 200}
    - contains: "OK"
  extract:
    userid: "$.data.list.[0].userId"

-
  name: 1
  feature: 管理用户
  story: 修改用户
  title: 修改用户接口
  request:
    url: /api/system/user/edit
    method: PUT
    json:
      "userId": ${userid}
      "deptId": "12"
      "userName": "testsa"
      "nickName": "test"
      "roleId": 3
      "remark": null
    headers:
      "Content-Type": "application/json"
      "Authorization": "Bearer ${access_token}"
  validate:
    - equals: {status_code: 200}
    - contains: "OK"

-
  name: 1
  feature: 管理用户
  story: 删除用户
  title: 删除用户接口
  request:
    url: /api/system/user/${userid}
    method: delete
    headers:
      "Content-Type": "application/json"
      "Authorization": "Bearer ${access_token}"
  validate:
    - equals: {status_code: 200}
    - contains: "OK"

4、实际效果展示

代码结构截图:

日志:

 pytest-html:

 allure报告:

后续可以自行安排将代码上传gitlab,联动jenkins;jenkins环境需要先部署好所有需要的环境和库

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值