自动化测试脚本的编写

走远了吗. 提交于 2020-02-13 21:23:17

需求:

  1. 从Excel中读取记录行(每一行就是一个API(url,请求类型,名称,描述,参数,预期值))

  2. 使用参数化对每一次的请求,要使用requests发请求,获取请求结果,从结果中提取字段,跟预期值做断言,

  3. 使用allure生成测试报告

    1. 为每一个请求用例添加title和description

  4. 将测试报告发邮件

    1. 问题:发邮件的时候,能发文件夹吗?答案是不能,

    2. 解决办法是:将allure报告文件夹打包成zip

    3. 发送zip文件

  5. 在你认为的关键点添加上log日志

    1. 请求的时候

    2. 断言的时候

    3. 可选打包的时候

    4. 读Excel的时候

  6. 为了解耦合,需要遵循软件开发规范

    1. 数据文件夹

    2. 配置文件夹

    3. 脚本文件夹

    4. 等等

  7. 如果写的困难得,可以在一个文件中实现

用到的知识点:

  1. requests

  2. pytest

  3. Excel表格操作

  4. 发邮件

  5. 日志功能

  6. 项目开发规范的目录应用

  7. allure

    1. title知识点

      2.description知识点
一、1.把软件开发规范的相关目录建立起来:

二、配置settings:

import osimport datetimeimport shutilimport sysbase_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))# print(base_dir) #D:\s27\day69\nb# 定义文件名字:file_name = "接口测试示例.xlsx"# 路径拼接:file_path = os.path.join(base_dir, "data", file_name)# print(file_path)    #D:\s27\day69\nb\data\接口测试示例.xlsx# ---------------- 日志相关 --------------------# 日志级别LOG_LEVEL = 'debug'# 屏幕输出流LOG_STREAM_LEVEL = 'debug'# 文件输出流LOG_FILE_LEVEL = 'info'# 日志文件命名LOG_FILE_NAME = os.path.join(base_dir, 'logs', datetime.datetime.now().strftime('%Y-%m-%d') + '.log')# allure报告相关:report_path = os.path.join(base_dir, "report")result_path = os.path.join(base_dir, "report", "result")allure_html_path = os.path.join(base_dir, "report", "allure_html")command = "allure generate {} -o {} --clean".format(result_path, allure_html_path)# 要打包的根目录:zip_case_result_path = allure_html_path# 要打包的文件名:zip_file_name = "allure_report.zip"# 将打包文件存放哪去:zip_save_path = report_path# 邮箱相关:# 第三方 SMTP 服务# 设置服务器mail_host = "smtp.qq.com"# 用户名mail_user = "1320685524@qq.com"# 获取授权码mail_pass = "mpaocydzpzfjidge"# 发件人账号sender = '1320685524@qq.com'# 接收邮件,可设置为你的QQ邮箱或者其他邮箱和多个收件人receivers = ['1320685524@qq.com']# 邮件主题:subject = 'Python发送带附件的邮件示例'# 邮件正文内容send_content = '这是今天的测试用例报告,请下载附件并使用pycharm打开'# 邮件附件路径:send_file_path = os.path.join(zip_save_path, zip_file_name)send_file_name = zip_file_nameif __name__ == '__main__':    # print(send_file_path)    pass    # os.remove(report_path)    # shutil.rmtree(report_path)    #查看路径:    # print(sys.path)    #查看包:    # print(sys.modules)

三、配置pytest.ini:

[pytest]addopts = -s -v --alluredir ./report/resulttestpaths = ./scriptspython_files = test_*.pypython_classes = Test*python_functions = test_*

四、data数据文件接口测试示例.xlsx:

五、logs日志文件:
六、report报告:
七、scripts测试用例:

import pytestimport allurefrom utils.ExcelHandler import Excelfrom utils.LogHandler import loggerfrom utils.RequestHandler import RequestHandlerfrom utils.AllureHandler import AllureHandler@pytest.mark.parametrize("d", Excel().get_excel_data())def test_case(d):    # print(d)    result = RequestHandler(d).get_response()    # logger().info(d)    allure.dynamic.title(d["case_project"])    allure.dynamic.description("<font color='red'>请求的URL:</font>{}<hr />"                               "<font color='red'>请求的类型:</font>{}<hr />"                               "<font color='red'>实际值:</font>{}<hr />"                               "<font color='red'>预期值:</font>{}<hr />"                               "".format(d["case_url"],                                         d["case_method"],                                         result[1],                                         result[0],                                         ))    assert result[0] == result[1]    # def teardown_module():    #     allure_obj = AllureHandler()    """生成allure测试报告"""    # allure_obj.execute_command()    """打包文件"""    # allure_obj.zip()    """发送邮件"""    # allure_obj.send_email()

八、AllureHandler.py报告功能:

"""执行allure命令生成报告打包allure报告发送压缩包"""import os# 导入压缩文件用的模块import zipfileimport smtplibimport shutilimport timefrom email.mime.text import MIMETextfrom email.mime.multipart import MIMEMultipartfrom email.header import Headerfrom conf import settingsfrom utils.LogHandler import loggerfrom subprocess import call, Popenclass AllureHandler(object):    def __init__(self):        """清空report目录,以便后续方便操作"""        # shutil.rmtree(settings.report_path)        pass    def execute_command(self):        """执行allure命令"""        # time.sleep(5)        logger().info("执行生成allure报告:{}".format(settings.command))        # os.system(settings.command)        # shell=True是可以识别字符串的命令:        call(settings.command, shell=True)    def zip(self):        """打包allure报告"""        # 日志:        logger().info("打包文件名:{},打包到:{}".format(settings.zip_file_name, settings.zip_save_path))        # 要压缩文件夹的根路径并拼接:        base_dir = settings.zip_case_result_path        zip_file_name = settings.zip_file_name        # 保存打包文件的路径:        f = zipfile.ZipFile(os.path.join(settings.zip_save_path, zip_file_name), 'w', zipfile.ZIP_DEFLATED)        for dir_path, dir_name, file_names in os.walk(base_dir):            # 要是不replace,就从根目录开始复制            file_path = dir_path.replace(base_dir, '')            # 实现当前文件夹以及包含的所有文件            file_path = file_path and file_path + os.sep or ''            for file_name in file_names:                f.write(os.path.join(dir_path, file_name), file_path + file_name)        f.close()    def send_email(self):        """将打包的allure文件发送给指定邮箱"""        logger().info("正在向{}发送邮件,请稍后.......".format(settings.receivers))        # 第三方SMTP服务        # 设置服务器        mail_host = settings.mail_host        # 用户名        mail_user = settings.mail_user        # 获取授权码        mail_pass = settings.mail_pass        # 发件人账号        sender = settings.sender        # 接收邮件,可设置为你的QQ邮箱或者其他邮箱和多个收件人        receivers = settings.receivers        # 创建一个带附件的实例        message = MIMEMultipart()        # 发件人        message['From'] = Header("我是发件人", 'utf-8')        # 收件人        message['To'] = Header("我是收件人", 'utf-8')        # 邮件主题        subject = settings.subject        message['Subject'] = Header(subject, 'utf-8')        # 邮件正文内容        send_content = settings.send_content        content_obj = MIMEText(send_content, 'plain', 'utf-8')        message.attach(content_obj)        # 构造附件1,发送当前目录下的文件        att1 = MIMEText(open(settings.send_file_path, 'rb').read(), "base64", "utf-8")        # 基于流的模式        att1["Content-Type"] = "application/octet-stream"        # 文件描述、filename是附件中显示的名字        att1["Content-Disposition"] = "attachment;filename = '{}'".format(settings.send_file_name)        message.attach(att1)        # 构造附件2,发送当前目录下的t2.py文件        # att2 = MIMEText(open("t2.py", "rb").read(), "base64", "utf-8")        #        # # 基于流的模式        # att2["Content-Type"] = "application/octet-stream"        #        # # 文件描述、filename是附件中显示的名字        # att2["Content-Disposition"] = "attachment;filename = 't2.py'"        # message.attach(att2)        try:            smtpObj = smtplib.SMTP()            # 25 为 SMTP 端口号            smtpObj.connect(mail_host, 25)            smtpObj.login(mail_user, mail_pass)            smtpObj.sendmail(sender, receivers, message.as_string())            logger().info("向{}发送邮件成功".format(settings.receivers))        except smtplib.SMTPException as e:            logger().error("向{}发送邮件失败,可能的原因:{}".format(settings.receivers, e))

九、ExcelHandler.py文件表功能:

import xlrdfrom conf import settingsfrom utils.LogHandler import loggerclass Excel(object):    def get_excel_data(self):        """获取excel表格数据"""        logger().info("读取Excel表格{} {}".format(settings.file_name,settings.file_path))        book = xlrd.open_workbook(filename=settings.file_path)        sheet = book.sheet_by_index(0)        # print(sheet.nrows)  #8        title = sheet.row_values(0)        l = []        for row in range(1, sheet.nrows):            # print(sheet.row_values(row))            l.append(dict(zip(title, sheet.row_values(row))))        # print(l)        return lif __name__ == '__main__':    Excel().get_excel_data()

十、LogHandler.py日志功能:

import loggingfrom conf import settingsclass 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):        """伪装成属性返回logger对象"""        return self.loggerdef logger(log_name='接口测试'):    return LoggerHandler(        log_name=log_name,        logger_level=settings.LOG_LEVEL,        file_name=settings.LOG_FILE_NAME,        stream_level=settings.LOG_STREAM_LEVEL,        file_level=settings.LOG_FILE_LEVEL    ).get_loggerif __name__ == '__main__':    logger().debug('aaaa')    logger().info('aaaa')    logger().warning('aaaa')

十一、RequestHandler.py请求功能:

import requestsimport jsonfrom utils.LogHandler import loggerfrom bs4 import BeautifulSoupclass RequestHandler(object):    def __init__(self, d):        self.d = d    def get_response(self):        """获取请求结果"""        return self.send_msg()    def _response_application(self, response):        """校验json类型的返回"""        response = response.json()        expect = json.loads(self.d.get("case_expect"))        for k in expect:            if expect[k] != response.get(k, "没有这个key:{}".format(k)):                return {k: expect[k]}, {k: response.get(k, "没有这个key:{}".format(k))}        return {k: expect[k]}, {k: response.get(k, "没有这个key:{}".format(k))}    def _response_text(self, response):        """校验文本类型的返回"""        # response.title()        soup = BeautifulSoup(response.text, "html.parser")        title = soup.find(name="title").text        # print(title.text)        logger().info("文本类型的请求结果,预期值:{} | 实际值:{}".format(self.d.get("case_expect"), title))        return title, self.d.get("case_expect")    def send_msg(self):        """发请求"""        logger().info("请求URL:{},类型:{}".format(self.d.get("case_url"), self.d.get("case_method")))        response = requests.request(            method=self.d.get("case_method"),            url=self.d.get("case_url"),            # params=self._check_params(),            # data=self._check_data()        )        header = response.headers["Content-Type"].split("/", 1)[0]        # header = _response_application        if hasattr(self, "_response_{}".format(header)):            a = getattr(self, "_response_{}".format(header))            result = a(response)        logger().info("预期值:{} 实际值:{}".format(result[0], result[1]))        return result    def _check_params(self):        """检查请求参数"""        params = self.d.get("case_params")        if params:            return {}        else:            return {}    def _check_data(self):        """ 检查请求参数 """        params = self.d.get("case_params")        if params:            return {}        else:            return {}if __name__ == '__main__':    # r1 = requests.get("https://www.cnblogs.com/Neeo/articles/10951734.html")    # print(r1.text)    # print(r1.headers)    # print(r1.title)    pass    # r2 = requests.get("https://www.v2ex.com/api/site/info.json")    # print(r2.headers)    # r3 = requests.post("https://cnodejs.org/api/v1/message/mark_all")    # print(r3.headers)

十二、run.py程序执行入口:

import shutilimport pytestfrom utils.AllureHandler import AllureHandlerfrom conf import settingsif __name__ == '__main__':    pytest.main()    # shutil.rmtree(r"D:\s27\day69包含log日志功能\nb\report")    a = AllureHandler()    a.execute_command()    a.zip()    a.send_email()

十三、效果展示:

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!