自动化测试做得好可以极大提高效率,为团队产品快速迭代把好质量关,但是也可能会成为鸡肋。很多企业项目不会配备齐全的自动化测试团队,要么一个人单打独斗,肩负所有自动化测试工作,要么功能测试人员兼顾自动化测试。这样就导致在版本转测节点手忙脚乱地分析、修复自动化用例,根本感受不到自动化带来的便利,反而成了累赘。导致这些问题的因素有很多,一个重要的因素是当前敏捷开发已是常态,需求变更频繁,导致自动化用例每个版本都难免要进行不同程度的调整,如果用例很多,一大片的失败用例定位原因就是挺耗时的工作。如何快速分析定位出自动化用例失败原因并及时修复是跟上敏捷开发节奏的关键。
pytest+selenium是UI自动化测试中常用的框架,再结合allure还可以生成漂亮的测试报告,失败的用例还可以分类查看错误日志,但是针对UI自动化来说,最直观的定位手段就是能看到报错时的页面截图,本文介绍如何实现失败用例自动截图并在allure测试报告中附上异常截图以方便测试人员快速定位问题。
钩子函数pytest_runtest_makereport
pytest提供了pytest_runtest_makereport这个方法,可以捕获用例的执行情况。根据官方提供的示例,在conftest.py文件中添加如下代码就可以捕获每个用例的执行结果。(官方示例链接:
https://docs.pytest.org/en/7.1.x/example/simple.html?highlight=pytest_runtest_makereport)
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
# execute all other hooks to obtain the report object
outcome = yield
rep = outcome.get_result() # rep可以拿到用例的执行结果详情
也就是说上面这部分代码,每个用例执行完成都会进入一次该方法,进来要做什么根据不同的目的自行编写代码实现,我们要做的是针对失败的用例进行截图。
截图并附加到allure报告中
接下来就是要添加异常截图的代码,通过rep.when和rep.failed判断只有用例执行失败的情况才会进行截图,注意实现截图这部分代码与pytest没有任何关系,你可以根据需要添加任何其他的代码。
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
# execute all other hooks to obtain the report object
outcome = yield
rep = outcome.get_result()
# 以下为实现异常截图的代码:
# rep.when可选参数有call、setup、teardown,
# call表示为用例执行环节、setup、teardown为环境初始化和清理环节
# 这里只针对用例执行且失败的用例进行异常截图
if rep.when == "call" and rep.failed:
# 检查driver对象是否包含get_screenshot_as_png方法
if hasattr(driver, "get_screenshot_as_png"):
# get_screenshot_as_png实现截图并生成二进制数据
# allure.attach直接将截图二进制数据附加到allure报告中
allure.attach(driver.get_screenshot_as_png(), "异常截图", allure.attachment_type.PNG)
说明:
1.allure.attach(body, name, attachment_type, extension)中body为附件的内容,name为附件名,attachment_type为附件类型,extension为扩展名。
2.driver为webdriver对象,代表用例执行中的使用的浏览器。
处理driver对象
这时你肯定会产生疑问driver对象从哪里来的,driver对象肯定是每个测试用例前创建的,就像访问页面必须先打开浏览器一样。然而,如果你是在原有的自动化工程基础上改造以增加异常截图的功能的话,driver对象的创建肯定是写在了别的地方了,这里就需要做下调整,要把打开浏览器对象代码一起放到conftest.py文件中,否则就抛出driver对象不存在的错误。
完整的conftest.py代码如下:
import pytest
import allure
from selenium import webdriver
driver = None
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
# execute all other hooks to obtain the report object
outcome = yield
rep = outcome.get_result()
if rep.when == "call" and rep.failed:
if hasattr(driver, "get_screenshot_as_png"):
allure.attach(driver.get_screenshot_as_png(), "异常截图", allure.attachment_type.PNG)
@pytest.fixture(scope="session")
def browser():
global driver
if driver is None:
driver = webdriver.Chrome()
driver.maximize_window()
yield driver
# 所有用例执行完毕退出浏览器
driver.quit()
- 使用@pytest.fixture装饰后的方法browser就成为了pytest固件;
- scope="session"表示固件作用范围可以跨.py文件,多个.py都可以使用该固件;
- browser虽然是全局固件,但是通过pytest-xdist插件并发运行的情况下仍然可以同时打开多个浏览器并行运行用例。
完整的示例
分别介绍完了实现异常自动截图的关键步骤,我们通过一个相对完整一些的示例来演示如何将其融入到自动化测试工程中,整体的目录结构如下
其中conftest.py就是上面提供的代码,test开头的为测试用例,其中一个用例文件的代码如下,通过在用例参数中传入browser来使用conftest.py中的固件。另外还在用例中故意添加失败的断言来让其中一个用例结果返回失败。
def test_访问bing首页(browser):
browser.get("https://www.bing.com")
assert True
def test_搜索pytest(browser):
# 访问bing
browser.get("https://www.bing.com")
# 输入关键字pytest
browser.find_element_by_xpath("//input[@id='sb_form_q']").send_keys("pytest")
# 点击搜索
browser.find_element_by_xpath("//label[@id='search_icon']").click()
assert False
运行全部用例后,allure报告中可以正常查看报错用例的异常截图。