测试学习记录,仅供参考!
自定义定制化展示 Allure 测试报告内容
allure.attach:添加附加信息
在Allure测试报告中,有时候需要添加一些附加信息,例如日志、截图等,以便更好地理解测试结果。pytest-Allure提供了allure.attach函数,可以方便地添加这些附加信息。
allure.attach
allure.attach用于在测试报告中添加附件,补充测试结果。附件格式可以是txt、jpg等,附件内容通常是测试数据、截图等。
allure.attach提供了两种方法:allure.attach(),allure.attach.file()
allure.attach()
作用:在测试报告中生成指定内容、名称、类型的附件
语法:allure.attach(body, name=None, attachment_type=None, extension=None)
参数说明:
body,需要显示的内容,也可以理解为写入附件的内容name,附件名称attachment_type,附件类型,如csv、jpg、html 等,由allure.attachment_type提供extension:附件扩展名,不常用
allure.attach.file()
作用:向测试用例中上传附件
语法:allure.attach.file(source, name=None, attachment_type=None, extension=None)
参数说明:source为文件路径,其他参数与allure.attach()参数一致。
在UI自动化测试中,会经常用到这个方法来上传用例执行的截图。
定制显示报告内容
添加显示更加详细的allure测试报告的内容;
1、 优化项目根目录 pageObject 软件包 other 目录下 file_upload_page.py 文件;
1)、引入 import allure 包(模块/第三方库)
2)、引入之后在合适的操作步骤中添加调用 allure.attach() 方法
3)、allure.attach(self.url, '打开测试页面', attachment_type=allure.attachment_type.TEXT)
第一个参数:传入要显示的内容;第二个参数:显示的标题;第三个参数:传显示的类型
4)、根据实际场景按需自行添加
5)、在 allure.attach() 里面调用截图的方法即可实现在报告中增加显示一个截图功能;之前在项目根目录 util_tools软件包目录basePage.py公共类方法中已经封装好了一个现成的 def screenshots(self, image_name): 截图方法;测试一下看看有木有效果;
6)、allure.attach(self.screenshots('files'), '文件上传截屏', achment_type=allure.attachment_type.PNG)
第一个参数: self.screenshots('files') 调用self.screenshots()里面传入一个文件名
第二个参数: 要显示的标题
第三个参数: attachment_type=allure.attachment_type.PNG 要保存的图片类型(根据实际需要选择)
python
# 导包
from selenium.webdriver.common.by import By
from util_tools.basePage import BasePage
from time import sleep
import allure
class FilesUploadPage(BasePage):
"""文件上传"""
# 外网地址--网络不稳定(建议隔一段时间再访问测试)
url = 'https://www.leafground.com/file.xhtml'
# 文件选择
files_input = (By.XPATH, '//*[@id="j_idt88:j_idt89_input"]')
# 断言结果
assert_result = (By.XPATH, '//*[@id="j_idt88:j_idt89"]/span[2]')
# 操作方法
def files_upload(self):
self.open_url(self.url)
# 第一个参数:传入显示的内容;第二个参数:显示的标题;第三个参数:传显示的类型
allure.attach(self.url, '打开测试页面', attachment_type=allure.attachment_type.TEXT)
# 设置要上传的文件路径(自行设置,若写到项目文件中可使用相对路径)--例如:固定的绝对路径(桌面文件)--前面加 r 防止被转义
file_path = r'C:\Users\Administrator\Desktop\文件上传.txt'
allure.attach(file_path, '上传文件', attachment_type=allure.attachment_type.TEXT)
# 上传文件
self.send_keys(self.files_input, file_path)
sleep(3)
allure.attach(self.screenshots('files'), '文件上传截屏', attachment_type=allure.attachment_type.PNG)
2、单独运行这个"文件上传"页面类对应的测试类(命令行运行或更改配置文件);
运行时有报错,再结合显示的"裂图"(图片打不开)说明截图不能这么使用;

3、详细查看 def screenshots(self, image_name) 方法后发现这个截图方法是要把一个文件保存到目标文件路径中的,截图图片已经固定保存相对应的文件中去了,不符合需要的结果;需要的是把截图结果放到报告中,所以需要再封装一个适用的方法。

实现步骤页面截图
添加显示更加详细的allure测试报告的内容,可以给每一个测试步骤添加一个截图,有截图就能追溯到测试结果、测试过程等;
封装方法
4、 首先在项目根目录 util_tools 软件包下 basePage.py 文件中封装一个新的截取图片方法;
1)、定义一个 def screenshots_png(self): 方法:保存为 PNG 图片格式,这里就不传文件名了;
2)、self.__driver:调用 .点 self.__driver 浏览器对象;
3)、self.__driver.get_screenshot_as_png:再调用 .get_screenshot_as_png 接收并保存为 png 格式的;
4)、self.__driver.get_screenshot_as_png():带上 () 小括号,调用这个方法;

5)、return self.__driver.get_screenshot_as_png():务必要把这个截图返回,return 一下。
python
def screenshots_png(self):
"""
页面截屏,保存为PNG格式
:return:
"""
return self.__driver.get_screenshot_as_png()
5、完整的 basePage.py 文件代码;
python
# 导包
from pyxnat.core.uriutil import file_path
from selenium import webdriver
from selenium.common import NoSuchElementException, TimeoutException
from selenium.webdriver.common.by import By
from time import sleep
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.wait import WebDriverWait
from config import setting
from util_tools.logs_util.recordlog import logs
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from datetime import datetime
import pytesseract
from PIL import Image
from selenium.webdriver.support.ui import Select
from util_tools.handle_data.configParse import ConfigParse
class BasePage(object):
def __init__(self, driver):
self.__driver = driver
self.__wait = WebDriverWait(self.__driver, setting.WAIT_TIME)
self.conf = ConfigParse()
def window_max(self):
self.__driver.maximize_window()
def window_full(self):
self.__driver.fullscreen_window()
def screenshot(self):
self.__driver.get_screenshot_as_png()
def open_url(self, url):
if url.startswith('http') or url.startswith('https'):
self.__driver.get(url)
logs.info(f'打开页面:{url}')
else:
new_url = self.conf.get_host('host') + url
self.__driver.get(new_url)
logs.info(f'打开页面:{new_url}')
def get_tag_text(self, locator: tuple):
try:
element = self.location_element(*locator)
element_text = element.text
logs.info(f'获取标签文本内容:{element_text}')
return element_text
except Exception as e:
logs.error(f'获取标签文本内容出现异常,原因为:{str(e)}')
@property
def current_url(self):
return self.__driver.current_url
@property
def title(self):
return self.__driver.title
def refresh(self):
self.__driver.refresh()
@property
def switch_to(self):
return self.__driver.switch_to
def iframe(self, frame):
try:
self.switch_to.frame(frame)
logs.info(f'切换到{frame}--iframe内部框架中')
except:
logs.error('切换到iframe框架失败!')
def switch_to_new_tab(self):
try:
original_window = self.__driver.window_handles[0]
all_window = self.__driver.window_handles
new_window = None
for window in all_window:
if window != original_window:
new_window = window
break
if new_window:
# self.__driver.switch_to.window(new_window)
self.switch_to.window(new_window)
logs.info('成功切换到新标签页')
except TimeoutException:
logs.error('等待新标签页打开超时。')
except NoSuchElementException:
logs.error('未找到新标签页句柄。')
except Exception as e:
logs.error(f'切换窗口时发生异常:{str(e)}')
def exit_iframe(self):
self.switch_to.default_content()
@property
def alert(self):
return self.__wait.until(ec.alert_is_present())
def alert_confirm(self):
self.alert.accept()
def alert_cancel(self):
"""点击alert弹框中的取消操作"""
self.alert.dismiss()
def location_element(self, by, value):
try:
element = self.__wait.until(ec.presence_of_element_located((by, value)))
logs.info(f"找到元素:{by}={value}")
return element
except Exception as e:
logs.error(f"未找到元素:{by}={value}")
raise e
def location_elements(self, by, value):
try:
self.__wait.until(ec.presence_of_all_elements_located((by, value)))
elements = self.__driver.find_elements(by, value)
logs.info(f"找到元素列表:{by}={value}")
return elements
except Exception as e:
logs.error(f"未找到元素列表:{by}={value}")
raise e
def click(self, locator: tuple, force=False):
try:
element = self.location_element(*locator)
if not force:
self.__driver.execute_script("arguments[0].click()", element)
else:
self.__driver.execute_script("arguments[0].click({force:true})", element)
logs.info(f"元素被点击:{locator}")
except NoSuchElementException as e:
logs.error(f"元素无法定位:{e}")
raise e
def send_keys(self, locator: tuple, data):
try:
element = self.location_element(*locator)
element.send_keys(data)
logs.info(f"元素被输入内容:{locator},输入的内容为:{data}")
except NoSuchElementException as e:
logs.error(f"元素无法定位:{e}")
raise e
def selects(self, locator: tuple, index):
try:
select = Select(self.location_element(*locator))
select.select_by_index(index)
logs.info(f'选择第{index}个数据')
except NoSuchElementException as e:
logs.error(f'元素无法定位:{e}')
raise e
def enter(self):
try:
ActionChains(self.__driver).send_keys(Keys.ENTER).perform()
logs.info("按下回车键")
except NoSuchElementException as e:
logs.error(f"元素无法定位:{e}")
raise e
def right_click(self, locator: tuple):
try:
element = self.location_element(*locator)
ActionChains(driver).context_click(element).perform()
logs.info("执行鼠标右键点击操作")
except NoSuchElementException as e:
logs.error(f"元素无法定位:{e}")
raise e
def double_click(self, locator: tuple):
try:
element = self.location_element(*locator)
ActionChains(driver).double_click(element).perform()
logs.info("执行鼠标双击操作")
except NoSuchElementException as e:
logs.error(f"元素无法定位:{e}")
raise e
def screenshots(self, image_name):
import os
current_time = datetime.now().strftime("%Y%m%d%H%M%S")
file_name = f"{image_name}-{current_time}.png"
file_path = os.path.join(setting.FILE_PATH.get('screenshot'), file_name)
self.__driver.get_screenshot_as_file(file_path)
def screenshots_png(self):
return self.__driver.get_screenshot_as_png()
def clear(self, locator: tuple):
try:
element = self.location_element(*locator)
element.clear()
logs.info("清空文本")
except NoSuchElementException as e:
logs.error(f"元素无法定位:{e}")
raise e
def ocr_captcha(self, locator: tuple):
captcha_element = self.location_element(*locator)
captcha_path = setting.FILE_PATH['screenshot'] + '/captcha.png'
captcha_element.screenshot(captcha_path)
captcha_image = Image.open(captcha_path)
try:
captcha_text = pytesseract.image_to_string(captcha_image)
logs.info(f"识别到的验证码为:{captcha_text}")
return captcha_text
except pytesseract.pytesseract.TesseractNotFoundError:
logs.error("找不到tesseract,这是因为pytesseract模块依赖于TesseractOCR引擎来进行图像识别!")
def is_element_present(self, locator: tuple):
try:
self.__wait.until(ec.presence_of_element_located(*locator))
return True
except:
return False
def assert_is_element_present(self, locator: tuple):
try:
element = self.__driver.find_element(*locator)
assert element.is_displayed(), '元素不存在'
except NoSuchElementException as e:
logs.error(f'元素未找到:{e}')
raise AssertionError('元素不存在')
def assert_element_not_visible(self, locator: tuple):
try:
self.__wait.until(ec.invisibility_of_element_located(locator))
except TimeoutException:
logs.error('元素可见')
def assert_title(self, expect_title):
assert expect_title in self.title
# 测试调试
if __name__ == '__main__':
driver = webdriver.Edge()
bros = BasePage(driver)
bros.open_url('https://www.leafground.com/alert.xhtml')
print(bros.current_url)
print(bros.title)
sleep(3)
使用
页面类通过 allure.acctch()调用即可
6、优化 file_upload_page.py 文件;根据实际需求场景自定义的去添加要显示的内容;
python
# 导包
from selenium.webdriver.common.by import By
from util_tools.basePage import BasePage
from time import sleep
import allure
class FilesUploadPage(BasePage):
"""文件上传"""
# 外网地址--网络不稳定(建议隔一段时间再访问测试)
url = 'https://www.leafground.com/file.xhtml'
# 文件选择
files_input = (By.XPATH, '//*[@id="j_idt88:j_idt89_input"]')
# 断言结果
assert_result = (By.XPATH, '//*[@id="j_idt88:j_idt89"]/span[2]')
# 操作方法
def files_upload(self):
self.open_url(self.url)
# 第一个参数:传入显示的内容;第二个参数:显示的标题;第三个参数:传显示的类型
allure.attach(self.url, '打开测试页面', attachment_type=allure.attachment_type.TEXT)
# 设置要上传的文件路径(自行设置,若写到项目文件中可使用相对路径)--例如:固定的绝对路径(桌面文件)--前面加 r 防止被转义
file_path = r'C:\Users\Administrator\Desktop\文件上传.txt'
allure.attach(file_path, '上传文件', attachment_type=allure.attachment_type.TEXT)
# 上传文件
self.send_keys(self.files_input, file_path)
sleep(3)
# 调用新封装的截图方法把截图添加到测试报告中
allure.attach(self.screenshots_png(), '文件上传截屏', attachment_type=allure.attachment_type.PNG)
7、单独运行查看,能够实现截屏功能,每条测试用例里面的测试步骤亦比较清晰明了;

8、用同样的方式去套用完善其他模块(例如登录、注册模块等--自行设置)
from selenium.webdriver.common.by import By
from util_tools.basePage import BasePage
import allure
# 登录页面类
class LoginPage(BasePage):
# url = 'http://localhost:8088/ecshop/user.php'
url = '/user.php'
# 用户名
username = (By.NAME, 'username')
# 密码
password = (By.NAME, 'password')
# 登录按钮
submit = (By.NAME, 'submit')
# 断言结果
assert_result = (By.XPATH, '//*[@id="ECS_MEMBERZONE"]/font/font')
# 登录操作
def login(self, user_name, pass_word):
# 打开网址
self.open_url(self.url)
# 第一个参数:传入显示的内容;第二个参数:显示的标题;第三个参数:传显示的类型
allure.attach(self.url, '打开登录测试页面', attachment_type=allure.attachment_type.TEXT)
# 输入用户名
self.send_keys(self.username, user_name)
# 输入密码
self.send_keys(self.password, pass_word)
# 点击登录按钮
self.click(self.submit)
allure.attach(self.screenshots_png(), f'输入账户:{user_name}, 密码:{pass_word}, 内容截屏', attachment_type=allure.attachment_type.PNG)
9、若是觉得 allure.attach() 方法会去频繁调用的话可以考虑封装到公共方法中统一去调用;
封装完之后调用,再传一个需要显示的内容就行了(烦请自行封装);
10、测试运作查看;


未完待续。。。