自動化測試腳本的編寫


需求:

  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 os
import datetime
import shutil
import sys

base_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 = "xxxx@qq.com"

# 獲取授權碼
mail_pass = "mpaocydzpzfjidge"

# 發件人賬號
sender = 'xxxx@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_name

if __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/result
testpaths = ./scripts
python_files = test_*.py
python_classes = Test*
python_functions = test_*

四、data數據文件接口測試示例.xlsx:

五、logs日志文件:
六、report報告:
七、scripts測試用例:

import pytest
import allure
from utils.ExcelHandler import Excel
from utils.LogHandler import logger
from utils.RequestHandler import RequestHandler
from 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 zipfile
import smtplib
import shutil
import time
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header
from conf import settings
from utils.LogHandler import logger
from subprocess import call, Popen


class 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 xlrd
from conf import settings
from utils.LogHandler import logger

class 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 l

if __name__ == '__main__':
Excel().get_excel_data()

十、LogHandler.py日志功能:

import logging
from conf import settings

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):
"""偽裝成屬性返回logger對象"""
return self.logger

def 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_logger

if __name__ == '__main__':
logger().debug('aaaa')
logger().info('aaaa')
logger().warning('aaaa')

十一、RequestHandler.py請求功能:

import requests
import json
from utils.LogHandler import logger
from bs4 import BeautifulSoup


class 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 shutil
import pytest
from utils.AllureHandler import AllureHandler
from conf import settings

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

十三、效果展示:

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM