zoukankan      html  css  js  c++  java
  • 接口测试框架-[pytest+requests+excel]读取excel表格+requests参数化+pytest测试报告

    用例示范

    目录结构

    1.demo

    demo1.py

    '''
    pip install xlrd
    pip install xlwt
    '''
    import xlrd
    import json
    import requests
    from bs4 import BeautifulSoup
    
    import xlrd
    
    # row 是行
    # col 是列
    
    file_path = r'接口测试示例.xlsx'
    
    # 拿到book对象 xlrd.open_workbook方法
    book = xlrd.open_workbook(file_path)
    print(book)
    
    # 拿到表格中对象 按索引获取
    sheet = book.sheet_by_index(0)
    print(sheet)
    
    # 按名字获取
    sheet1 = book.sheet_by_name('接口自动化用例')
    print(sheet1)
    
    # 行,列 获取到所有的行和列 sheet.nrows方法, sheet.ncols方法
    rows, cols = sheet.nrows, sheet.ncols
    print(rows, cols)
    
    # for循环出每一行
    for row in range(rows):
        print(sheet.row_values(row))
    
    # for循环出每一列
    for col in range(cols):
        print(sheet.col_values(col))
    
    # 按索引取出行列
    print(sheet.row_values(2))
    
    print(sheet.col_values(0))
    
    print(sheet.cell(0, 0))
    
    # 将行和列的值拼接在一起 组合成[{},{}]的样式 zip拉在一起 dict转一下
    l = []
    title = sheet.row_values(0)
    for i in range(1, rows):
        l.append(dict(zip(title, sheet.row_values(i))))
    
    print(l)
    
    
    def get_excel_data():
        file_path = r'接口测试示例.xlsx'
    
        # 获取到book对象
        book = xlrd.open_workbook(file_path)
        # print(book)
        # 获取sheet对象
        sheet = book.sheet_by_index(0)
        # sheet = book.sheet_by_name('接口自动化用例')
        # sheets = book.sheets()  # 获取所有的sheet对象
    
        rows, cols = sheet.nrows, sheet.ncols
        l = []
        # print(sheet.row_values(0))
        title = sheet.row_values(0)
        # print(title)
        # 获取其他行
        for i in range(1, rows):
            # print(sheet.row_values(i))
            l.append(dict(zip(title, sheet.row_values(i))))
    
        return l
    r = requests.get('https://www.cnblogs.com/Neeo/articles/11667962.html')
    s = BeautifulSoup(r.text, 'html.parser')
    print(s.find('title').text)
    

    2.uti

    ExcelHandler

    '''
    
    关于Excel表的操作
    '''
    
    import xlrd
    from settings import conf
    
    
    class ExcelHandler(object):
    
        @property
        def get_excel_data(self):
            # 获取到book对象
            book = xlrd.open_workbook(conf.TEST_CASE_PATH)
            # print(book)
            # 获取sheet对象
            sheet = book.sheet_by_index(0)
            # sheet = book.sheet_by_name('接口自动化用例')
            # sheets = book.sheets()  # 获取所有的sheet对象
    
            rows, cols = sheet.nrows, sheet.ncols
            l = []
            # print(sheet.row_values(0))
            title = sheet.row_values(0)
            # print(title)
            # 获取其他行
            for i in range(1, rows):
                # print(sheet.row_values(i))
                l.append(dict(zip(title, sheet.row_values(i))))
            return l
    
    

    RequestsHandler.py

    
    '''
    
    请求相关
    '''
    
    import json
    import requests
    from bs4 import BeautifulSoup
    from uti.LoggerHandler import logger
    
    
    class RequestHandler(object):
    
        def __init__(self, case):
            self.case = case
            try:
                self.case_expect = json.loads(self.case['case_expect'])
            except:
                self.case_expect = self.case['case_expect']
    
        @property
        def get_response(self):
            """ 获取请求结果 """
            response = self.send_request()
            return response
    
        def send_request(self):
            """ 发请求 """
            try:
                response = requests.request(
                    method=self.case['case_method'],
                    url=self.case['case_url'],
                    params=self._check_params()
                )
                content_type = response.headers['Content-Type']
                content_type = content_type.split(";")[0].split('/')[-1] if ';' in content_type else 
                content_type.split("/")[-1]
                if hasattr(self, '_check_{}_response'.format(content_type)):
                    response = getattr(self, '_check_{}_response'.format(content_type))(response)
                else:
                    raise '返回类型为: {}, 无法解析'.format(content_type)
            except:
                logger().error({'response': "请求发送失败,详细信息: url={}".format(self.case['case_url'])})
                return {'response': "请求发送失败,详细信息: url={}".format(self.case['case_url'])}, self.case['case_expect']
    
            return response
    
        def _check_json_response(self, response):
            """  处理json类型的返回值 """
            response = response.json()  # {'success': True}
            for key in self.case_expect:
                if self.case_expect[key] != response[key]:  # 用例执行失败的
                    return {key: self.case_expect[key]}, {key: response[key]}
            else:  # 执行成功
                logger("发送请求").info('{} 执行成功'.format(self.case['case_url']))
                return {key: self.case_expect[key]}, {key: response[key]}
    
        def _check_html_response(self, response):
            """ 校验html类型的数据"""
            soup_obj = BeautifulSoup(response.text, 'html.parser')
            title = soup_obj.find('title').text
            return title, self.case_expect
    
        def _check_params(self):
            """ 整理参数 """
            if self.case['case_params']:
                """
                做扩展
                """
                pass
            else:
                return {}
    

    AllureHandler

    import subprocess
    from settings import conf
    
    class AllureHandler(object):
    
        def execute_command(self):
            import time
            time.sleep(1)
            subprocess.call(conf.ALLURE_COMMAND, shell=True)
    

    EmailHandler

    
    '''
    发邮件
    '''
    import smtplib
    from email.mime.text import MIMEText
    from email.mime.multipart import MIMEMultipart
    from email.header import Header
    from settings import conf
    
    from uti.LoggerHandler import logger
    
    
    class EmailHandler(object):
    
        def read_report(self):
            f = open(conf.TEST_CASE_REPORT_PATH, 'rb')
            return f.read()
    
        def send_email(self):
            """ 发送邮件 """
    
            # 第三方 SMTP 服务
            mail_host = "smtp.qq.com"  # 设置服务器
            mail_user = "xxx@qq.com"  # 用户名
            mail_pass = "xxx"  # 口令
            # 获取口令地址 https://www.cnblogs.com/zhangshan33/p/11943755.html
    
            # 设置收件人和发件人
            sender = 'xxx@qq.com'
            receivers = ['xxx@qq.com', ]  # 接收邮件,可设置为你的QQ邮箱或者其他邮箱
    
            # 创建一个带附件的实例对象
            message = MIMEMultipart()
    
            # 邮件主题、收件人、发件人
            subject = '请查阅--测试报告'  # 邮件主题
            message['Subject'] = Header(subject, 'utf-8')
            message['From'] = Header("{}".format(sender), 'utf-8')  # 发件人
            message['To'] = Header("{}".format(';'.join(receivers)), 'utf-8')  # 收件人
    
            # 邮件正文内容 html 形式邮件
            send_content = self.read_report()  # 获取测试报告
            html = MIMEText(_text=send_content, _subtype='html', _charset='utf-8')  # 第一个参数为邮件内容
    
            # 构造附件
            att = MIMEText(_text=send_content, _subtype='base64', _charset='utf-8')
            att["Content-Type"] = 'application/octet-stream'
            file_name = 'report.html'
            att["Content-Disposition"] = 'attachment; filename="{}"'.format(file_name)  # # filename 为邮件附件中显示什么名字
            message.attach(html)
            message.attach(att)
    
            try:
                smtp_obj = smtplib.SMTP()
                smtp_obj.connect(mail_host, 25)  # 25 为 SMTP 端口号
                smtp_obj.login(mail_user, mail_pass)
                smtp_obj.sendmail(sender, receivers, message.as_string())
                smtp_obj.quit()
                logger().info("邮件发送成功")
    
            except smtplib.SMTPException:
                logger().error("Error: 无法发送邮件")
    
    

    LoggerHandler

    import logging
    from settings import conf
    
    
    class LoggerHandler:
        """ 日志操作 """
        _logger_level = {
            'debug': logging.DEBUG,
            'info': logging.INFO,
            'warning': logging.WARNING,
            'error': logging.ERROR,
            'critical': logging.CRITICAL
        }
    
        def __init__(self, log_name, file_name, logger_level, stream_level='info', file_level='warning'):
            self.log_name = log_name
            self.file_name = file_name
            self.logger_level = self._logger_level.get(logger_level, 'debug')
            self.stream_level = self._logger_level.get(stream_level, 'info')
            self.file_level = self._logger_level.get(file_level, 'warning')
            # 创建日志对象
            self.logger = logging.getLogger(self.log_name)
            # 设置日志级别
            self.logger.setLevel(self.logger_level)
            if not self.logger.handlers:
                # 设置日志输出流
                f_stream = logging.StreamHandler()
                f_file = logging.FileHandler(self.file_name)
                # 设置输出流级别
                f_stream.setLevel(self.stream_level)
                f_file.setLevel(self.file_level)
                # 设置日志输出格式
                formatter = logging.Formatter(
                    "%(asctime)s %(name)s %(levelname)s %(message)s"
                )
                f_stream.setFormatter(formatter)
                f_file.setFormatter(formatter)
                self.logger.addHandler(f_stream)
                self.logger.addHandler(f_file)
    
        @property
        def get_logger(self):
            return self.logger
    
    
    def logger(log_name='接口测试'):
        return LoggerHandler(
            log_name=log_name,
            logger_level=conf.LOG_LEVEL,
            file_name=conf.LOG_FILE_NAME,
            stream_level=conf.LOG_STREAM_LEVEL,
            file_level=conf.LOG_FILE_LEVEL
        ).get_logger
    
    
    if __name__ == '__main__':
        logger().debug('aaaa')
        logger().info('aaaa')
        logger().warning('aaaa')
    
    

    3.scripts

    test_case

    import pytest
    import allure
    from uti.ExcelHandler import ExcelHandler
    from uti.RequestHandler import RequestHandler
    from uti.AllureHandler import AllureHandler
    from uti.EmailHandler import EmailHandler
    '''
    1. 拿到Excel数据
    2. 发请求
    3. 生成测试用例报告
    4. 发邮件
    5. 断言
    
    '''
    
    
    class Test_case(object):
        @pytest.mark.parametrize('case', ExcelHandler().get_excel_data)
        def test_case(self, case):
            """  执行断言 """
            # print(case)
            # 发请求
            response = RequestHandler(case).get_response
    
            # 制作 allure 报告
            allure.dynamic.title(case['case_project'])
            allure.dynamic.description('<font color="red">请求URL:</font>{}<br />'
                                       '<font color="red">期望值:</font>{}'.format(case['case_url'], case['case_description']))
            allure.dynamic.feature(case['case_project'])
            allure.dynamic.story(case['case_method'])
            assert response[0] == response[1]
    
        def teardown_class(self):
            """  执行alllure命令 """
    
            AllureHandler().execute_command()
            # 发邮件
            EmailHandler().send_email()
    

    4.settings

    conf.py

    
    import os
    import datetime
    
    BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    
    # 脚本路径
    file_path = '接口测试示例.xlsx'
    TEST_CASE_PATH = os.path.join(BASE_PATH, 'data', file_path)
    
    # 报告路径
    TEST_CASE_REPORT_PATH = os.path.join(BASE_PATH, 'report', 'report.html')
    
    # CASE_METHOD = 'case_method'
    
    
    # ------------ allure 相关配置 -----------
    
    result_path = os.path.join(BASE_PATH, 'report', 'result')
    allure_html_path = os.path.join(BASE_PATH, 'report', 'allure_html')
    ALLURE_COMMAND = 'allure generate {} -o {} --clean'.format(result_path, allure_html_path)
    
    # ---------------- 日志相关 --------------------
    # 日志级别
    LOG_LEVEL = 'debug'
    LOG_STREAM_LEVEL = 'debug'  # 屏幕输出流
    LOG_FILE_LEVEL = 'info'  # 文件输出流
    
    # 日志文件命名
    
    LOG_FILE_NAME = os.path.join(BASE_PATH, 'logs', datetime.datetime.now().strftime('%Y-%m-%d') + '.log')
    
    if __name__ == '__main__':
        print(TEST_CASE_PATH)
    

    5.pytest.ini

    [pytest]
    addopts = -s -v --html=report/report.html --alluredir ./report/result
    testpaths = ./scripts
    python_files = test_*.py
    python_classes = Test*
    python_functions = test_*
    

    6.run.py

    import pytest
    
    if __name__ == '__main__':
        pytest.main()
    

    7.直接右击运行run.py文件


    8.终端运行allure

    allure generate report/result -o report/allure_html --clean

    生成的测试报告

    用浏览器打开

    要发送的报告

    用浏览器打开

    9.邮箱收到的文件


    GitHub代码

    https://github.com/zhangshan33/ATScripts

  • 相关阅读:
    effective C++
    bat取时间间隔
    bat设置windows计划任务
    listener.ora 与 tnsnames.ora
    route(windows)
    bat 数组实现
    非const引用参数传入不同类型编译不过的理解(拒绝将临时对象绑定为非const的引用的形参是有道理的)
    python no module named builtins
    Caffe使用新版本CUDA和CuDNN
    Ubuntu16.04安装vim8
  • 原文地址:https://www.cnblogs.com/zhangshan33/p/11940356.html
Copyright © 2011-2022 走看看