python 接口自动化unittest、ddt

今天为大家分享一个接口自动化的框架!

1、框架主要功能:

  1. 请求接口
  2. 生成HTML测试报告
  3. 发送附件邮件
  4. 消息推送至钉钉群
  5. 生成log便于查看执行情况

2、框架:

python 接口自动化unittest、ddt_第1张图片
Asser:断言模块(这里封装的是判断,用于判断预期与返回是否一致)
CaseFile:用例文件(存放用例所需的文件夹)
Common:发送邮件、钉钉推送、HTML、生成log、接口请求的模块
result:存放生成的好的log、HTML
testCase:执行模块
config:配置文件,配置其他模块所需的值
CreateHTML:执行生成HTML、发送邮件、发送叮叮推送消息
ReadConfig:获取config 配置文件的内容
ReadExcel:读取用例的模块
ReadPath:获取当前路径的模块
Return_Headers:生成所需的请求头,便于依赖接口执行

3、模块代码:

Assertions:断言模块

这里验证了两种统一的;即每个返回值中都有的字段

from Uinttest.Uinttest_Two.Common.Log import logger

'''
断言 模块

'''


class Assertions1(object):

    def Values_Code(self, Default, return_va):
        '''
        验证返回code状态码
        :param return_va:
        :return:
        '''
        try:
            assert Default == return_va
            logger.info("True:Interface return value verification:%s : %s" % (Default, return_va))
            print("验证成功,code码为:%s === %s" % (Default, return_va))
        except Exception as e:
            logger.info("False:Interface return value verification:%s : %s" % (Default, return_va))
            print("验证失败,code码为:%s === %s" % (Default, return_va))
            raise e

    def Values_Data(self, Default, return_va):
        '''
        验证返回data信息
        :param return_va:
        :return:
        '''
        try:
            assert Default == return_va
            logger.info("True:Interface return value verification:%s : %s" % (Default, return_va))
            print("验证成功,data值为:%s === %s" % (Default, return_va))
        except Exception as e:
            logger.info("False:Interface return value verification:%s : %s" % (Default, return_va))
            print("验证失败,data值为:%s === %s" % (Default, return_va))
            raise e

CaseFile 文件夹:

在这里插入图片描述
编写的用例:
包含了URL、请求值、测试模块、用例名称、预期code,预期值

ConfigEmail 发送邮件:

import os
import smtplib
import base64
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from Uinttest.Uinttest_Two.ReadConfig import Email_config
'''
用于发送邮件  QQ

'''


class SendEmail(object):
    def __init__(self, file=None, ssl=False, email_host='smtp.qq.com', port=25, ssl_port=465):
        self.file = file  # 附件路径,如果不在当前目录下,要写绝对路径
        self.email_host = email_host  # smtp服务器地址
        self.port = port  # 普通端口
        self.ssl = ssl  # 是否安全链接
        self.ssl_port = ssl_port  # 安全链接端口

    def send_email(self):
        msg = MIMEMultipart()
        # 发送内容的对象
        if self.file:  # 处理附件的
            file_name = os.path.split(self.file)[-1]  # 只取文件名,不取路径
            try:
                f = open(self.file, 'rb').read()
            except Exception as e:
                raise Exception('附件打不开!!!!', e)
            else:
                att = MIMEText(f, "base64", "utf-8")
                att["Content-Type"] = 'application/octet-stream'
                file_name = '=?utf-8?b?' + base64.b64encode(file_name.encode()).decode() + '?='
                # 这里是处理文件名为中文名的,必须这么写
                att["Content-Disposition"] = 'attachment; filename="%s"' % (file_name)
                msg.attach(att)
        msg.attach(MIMEText("接口自动化测试邮件,请查看下方附件....."))  # 邮件正文的内容
        msg['Subject'] = "接口自动化测试邮件"  # 邮件主题
        msg['From'] = Email_config("addresser")  # 发送者账号
        msg['To'] = ','.join(Email_config("addressee"))  # 接收者账号列表
        if self.ssl:
            self.smtp = smtplib.SMTP_SSL(self.email_host, port=self.ssl_port)
        else:
            self.smtp = smtplib.SMTP(self.email_host, port=self.port)
        # 发送邮件服务器的对象
        self.smtp.login(Email_config("addresser"), Email_config("password"))
        try:
            self.smtp.sendmail(Email_config("addresser"), Email_config("addressee"), msg.as_string())
            pass
        except Exception as e:
            print('出错了。。', e)
        else:
            print('发送成功!')
        self.smtp.quit()

#  验证是否可以发送成功
if __name__ == '__main__':
    m = SendEmail(
        file=r'/Users/a123/Desktop/Automation/Uinttest/Uinttest_Two/result/logs',
        ssl=True,
    )
    m.send_email()

DingDing 推送钉钉群消息:

具体配置可参考我的另一篇文章:
https://blog.csdn.net/weixin_44750991/article/details/107022885

import time
import hmac
import urllib
import hashlib
import base64
from urllib import parse
from dingtalkchatbot.chatbot import DingtalkChatbot
from Uinttest.Uinttest_Two.ReadConfig import Ding_Config
'''
用于钉钉推送消息

'''


class DING(object):

    def getSIGN(self):
        timestamp = str(round(time.time() * 1000))
        urlToken = eval(Ding_Config("Token"))
        secret = eval(Ding_Config("secret"))
        secret_enc = secret.encode('utf-8')
        string_to_sign = '{}\n{}'.format(timestamp, secret)
        string_to_sign_enc = string_to_sign.encode('utf-8')
        hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
        sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))

        SignMessage = urlToken + "×tamp=" + timestamp + "&sign=" + sign
        return SignMessage

    def chu_shi(self):
        self.getSIGN()
        SignMessage = self.getSIGN()
        self.xiaoDing = DingtalkChatbot(SignMessage)  # 初始化机器人

    def tsy(self):
        self.chu_shi()
        self.xiaoDing.send_text("接口自动化执行完成,请至邮箱中文件附件查看执行情况!", is_at_all=False)

#  验证是否可以推送成功
if __name__ == "__main__":
    DING().tsy()

HTMLTestRunner

直接网盘下载后放入文件夹就行:
(注:这个是python3中使用的)

链接: https://pan.baidu.com/s/1BPSH5k4LGtt35-RbXFM1CA  密码: 5g0l

log 生成日志:

import os
import logging
from logging.handlers import TimedRotatingFileHandler
from Uinttest.Uinttest_Two.ReadPath import GetPath
log_path = os.path.join(GetPath(), 'result')  # 存放log文件的路径
'''
用于生成log

'''


class Logger(object):
    def __init__(self, logger_name='logs…'):
        self.logger = logging.getLogger(logger_name)
        logging.root.setLevel(logging.NOTSET)
        self.log_file_name = 'logs'  # 日志文件的名称
        self.backup_count = 5  # 最多存放日志的数量
        # 日志输出级别
        self.console_output_level = 'WARNING'
        self.file_output_level = 'DEBUG'
        # 日志输出格式
        self.formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

    def get_logger(self):
        """在logger中添加日志句柄并返回,如果logger已有句柄,则直接返回"""
        if not self.logger.handlers:  # 避免重复日志
            console_handler = logging.StreamHandler()
            console_handler.setFormatter(self.formatter)
            console_handler.setLevel(self.console_output_level)
            self.logger.addHandler(console_handler)

            # 每天重新创建一个日志文件,最多保留backup_count份
            file_handler = TimedRotatingFileHandler(filename=os.path.join(log_path, self.log_file_name), when='D',
                                                    interval=1, backupCount=self.backup_count, delay=True,
                                                    encoding='utf-8')
            file_handler.setFormatter(self.formatter)
            file_handler.setLevel(self.file_output_level)
            self.logger.addHandler(file_handler)
        return self.logger


logger = Logger().get_logger()

Port_request 请求接口:

import requests
import json
'''
封装的请求接口模块

'''


class Port(object):

    def Mall_port(self, url, data, head):
        try:
            post = requests.post(url=url, data=data, headers=head).json()
            rest = json.dumps(post, ensure_ascii=False, sort_keys=True, indent=2)
            return rest
        except Exception as w:
            raise ValueError("接口请求失败!", w)
        
#  填入请求内容,查看请求是否正常
if __name__ == "__main__":
    Port().Mall_port(url="", data="", head="")

config.ini 配置文件:

[HEADERS]
url = http://******/xz/data/user/login/doLogin
data = {
     "userName":"******","password":"******"}


[EMAIL]
#  开关
on_off = on_off
subject = 接口自动化测试报告
app = Outlook
#  收件人
addressee = ****@qq.com
#  发件人
addresser = *****@qq.com
#  密码
password = *****


[DING]
secret = 'SEC0042920bb9eb*************'
Token = "https://oapi.dingtalk.com/robot/send?access_token=*************"

CreateHTML 代码:

import ddt
import unittest
import re
from Uinttest.Uinttest_Two.ReadExcel import get_xls  # 用例模块
from Uinttest.Uinttest_Two.Common.Port_request import Port  # 接口请求模块
from Uinttest.Uinttest_Two.Return_Headers import heads  # 请求头模块
from Uinttest.Uinttest_Two.Asser.Assertions import Assertions1  # 断言模块
from Uinttest.Uinttest_Two.CreateHTML import create  # 生成HTML

xls = get_xls('doLogin.xlsx', 'Sheet1')

'''
使用ddt处理读取的文件中数据,并执行接口请求

'''


@ddt.ddt
class readExcel(unittest.TestCase):

    def setUp(self):
        """
        :return:
        """

        print("----测试开始前准备----")

    def tearDown(self):
        print("测试结束,输出log完结\n\n")

    @ddt.data(*xls)
    @ddt.unpack
    def test1(self, url, data, module, case, code, verify):
        try:
            print("测试内容为:%s---%s" % (module, case))
            post = Port().Mall_port(url=url, data=data, head=heads())
            print(post)
            #  判断返回的  code  是否正确
            return_code = re.findall(r'"code": (.+?),', post)
            Assertions1().Values_Code(int(return_code[0]), int(code))

            #  判断返回的  data  是否正确
            return_message = re.findall(r'"message": "(.+?)"', post)
            if return_message != []:
                Assertions1().Values_Data(return_message[0], verify)
            else:
                print("message值为空!")
        except Exception as s:
            raise ValueError("执行接口失败!", s)


'''
生成HTML、发送邮件、发送叮叮推送消息

'''

create(readExcel)

ReadConfig 获取配置文件信息:

import os
import configparser
from Uinttest.Uinttest_Two.ReadPath import GetPath
'''
拼接链接 获取链接中的内容

return config values
'''
path = os.path.join(GetPath(), "config.ini")
# 调用外部的读取配置文件的方法
config = configparser.ConfigParser()
config.read(path, encoding='utf-8')
'''
获取config 中headers中的数据
'''


def headers_config(va):

    value = config.get("HEADERS", va)
    return value


'''
获取 config 中 email 中的数据
'''


def Email_config(va):
    value = config.get("EMAIL", va)
    return value


'''
获取 config 中 叮叮 中的数据

'''


def Ding_Config(va):
    value = config.get("DING", va)
    return value

ReadExcel 读取Excel中的用例:

import os
from Uinttest.Uinttest_Two.ReadPath import GetPath
# 调用读Excel的第三方库xlrd
from xlrd import open_workbook
'''
return xlsx values

读取文件,并返回内容
'''


def get_xls(FileName, filename):  # xls_name填写用例的Excel名称 sheet_name该Excel的sheet名称
    csv = []
    # 获取用例文件路径
    xlsPath = os.path.join((GetPath() + "/CaseFile"), FileName)
    file = open_workbook(xlsPath)  # 打开用例Excel
    sheet = file.sheet_by_name(filename)  # 获得打开Excel的sheet
    # 获取这个sheet内容行数
    now = sheet.nrows
    for i in range(now):  # 根据行数做循环
        csv.append(sheet.row_values(i))
    return csv

ReadPath 获取当前项目的路径:

import os
'''
return path

获取当前文件的路径,并返回
'''


def GetPath():

    getpath = os.path.split(os.path.realpath(__file__))[0]

    return getpath

Return_Headers 封装依赖时所需的请求头:

import re
from Uinttest.Uinttest_Two.Common.Port_request import Port
from Uinttest.Uinttest_Two.ReadConfig import headers_config
'''
return headers


设置的请求头
'''


def heads(value=None):

    new = {
     
            "Accept": "*/*",
            "Accept-Encoding": "gzip, deflate",
            "Accept-Language": "zh-CN,zh;q=0.9",
            "Connection": "keep-alive",
            "Content-Length": "48",
            "Content-type": "application/json",
            "Host": "*******:9099",
            "Origin": "http://*******:9099",
            "Referer": "http://********:9099/",
            "source": "wmdn_pc",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36",
            "Authorization": value
        }
    return new


def __ret():
    post = Port().Mall_port(headers_config("url"), headers_config("data"), heads())
    return_token = re.findall(r'"accessToken": "(.+?)"', post)
    return return_token[0]


'''
返回配置好的请求头

return headers

'''


def return_head():

    return heads(__ret())


# print(return_head())

Run 执行接口自动化:

import ddt
import unittest
import re
from Uinttest.Uinttest_Two.ReadExcel import get_xls  # 用例模块
from Uinttest.Uinttest_Two.Common.Port_request import Port  # 接口请求模块
from Uinttest.Uinttest_Two.Return_Headers import heads  # 请求头模块
from Uinttest.Uinttest_Two.Asser.Assertions import Assertions1  # 断言模块
from Uinttest.Uinttest_Two.CreateHTML import create  # 生成HTML

xls = get_xls('doLogin.xlsx', 'Sheet1')

'''
使用ddt处理读取的文件中数据,并执行接口请求

'''


@ddt.ddt
class readExcel(unittest.TestCase):

    def setUp(self):
        """
        :return:
        """

        print("----测试开始前准备----")

    def tearDown(self):
        print("测试结束,输出log完结\n\n")

    @ddt.data(*xls)
    @ddt.unpack
    def test1(self, url, data, module, case, code, verify):
        try:
            print("测试内容为:%s---%s" % (module, case))
            post = Port().Mall_port(url=url, data=data, head=heads())
            print(post)
            #  判断返回的  code  是否正确
            return_code = re.findall(r'"code": (.+?),', post)
            Assertions1().Values_Code(int(return_code[0]), int(code))

            #  判断返回的  data  是否正确
            return_message = re.findall(r'"message": "(.+?)"', post)
            if return_message != []:
                Assertions1().Values_Data(return_message[0], verify)
            else:
                print("message值为空!")
        except Exception as s:
            raise ValueError("执行接口失败!", s)


'''
生成HTML、发送邮件、发送叮叮推送消息

'''

create(readExcel)

执行后的log:
python 接口自动化unittest、ddt_第2张图片
执行后的HTML:
python 接口自动化unittest、ddt_第3张图片

本文有参考于:

https://blog.csdn.net/songlh1234/article/details/84317617?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param

以上就是全部!如有问题,欢迎留言讨论!

你可能感兴趣的:(python,python,unittest,ddt)