• 自动化测试中的失败截图和存log


    如果我们在执行自动化测试的时候,希望能在失败的时候保存现场,方便事后分析。 对于UI自动化,我们希望截图在测试报告中。 对于api自动化,我们希望截取出错的log在测试报告中。 我开始自己蛮干,写了两个出错截图的方法。

    1. def save_screenshot():
    2. '''
    3. 页面截屏保存截图
    4. :return:
    5. '''
    6. file_name = IMG_PATH + "\\{}.png".format(time.strftime("%Y%m%d%H%M%S", time.localtime()))
    7. d.screenshot(file_name)
    8. with open(file_name, mode='rb') as f:
    9. file = f.read()
    10. allure.attach(file, allure.attachment_type.PNG)

    出错截图,我写了一个装饰器

    1. def fail_screenshot(func):
    2. '''
    3. 失败页面截屏保存截图
    4. :return:
    5. '''
    6. def wrapper(*args, **kwargs):
    7. try:
    8. func(*args, **kwargs)
    9. except:
    10. file_name = FAIL_IMG_PATH + "\\{}_{}.png".format(func.__name__,
    11. time.strftime("%Y%m%d%H%M%S", time.localtime()))
    12. d.screenshot(file_name)
    13. # with open(file_name, mode='rb') as f:
    14. # file = f.read()
    15. # allure.attach(file, allure.attachment_type.PNG)
    16. return wrapper

    似乎能达到我的期望,就是太烦了,每次需要调用或者将装饰器写在函数上。 然后我发现allue里面有一个钩子函数。

    1. from _pytest import runner
    2. # 对应源码
    3. def pytest_runtest_makereport(item, call):
    4. """ return a :py:class:`_pytest.runner.TestReport` object
    5. for the given :py:class:`pytest.Item` and
    6. :py:class:`_pytest.runner.CallInfo`.
    7. """

    这里item是测试用例,call是测试步骤,具体执行过程如下:

    先执行when='setup' 返回setup 的执行结果

    然后执行when='call' 返回call 的执行结果

    最后执行when='teardown'返回teardown 的执行结果

    例如:

    1. # conftest.py
    2. import pytest
    3. @pytest.hookimpl(hookwrapper=True, tryfirst=True)
    4. def pytest_runtest_makereport(item, call):
    5. print('------------------------------------')
    6. # 获取钩子方法的调用结果
    7. out = yield
    8. print('用例执行结果', out)
    9. # 3. 从钩子方法的调用结果中获取测试报告
    10. report = out.get_result()
    11. print('测试报告:%s' % report)
    12. print('步骤:%s' % report.when)
    13. print('nodeid:%s' % report.nodeid)
    14. print('description:%s' % str(item.function.__doc__))
    15. print(('运行结果: %s' % report.outcome))

    运行用例的过程会经历三个阶段:setup-call-teardown,每个阶段都会返回的 Result 对象和 TestReport 对象,以及对象属性。

    如果setup执行失败了,setup的执行结果的failed,后面的call用例和teardown都不会执行了。

    如果setup正常执行,但是测试用例call失败了。那么此时运行的结果就是failed。

    如果setup正常执行,测试用例call正常执行,teardown失败了,这种情况,最终统计的结果:1 passed, 1 error in 0.16 seconds

    只获取call的时候,我们在写用例的时候,如果保证setup和teardown不报错情况,只关注测试用例本身的运行结果,前面的 pytest_runtest_makereport 钩子方法执行了三次。

    可以加个判断:if report.when == "call"

    1. import pytest
    2. from _pytest import runner
    3. '''
    4. # 对应源码
    5. def pytest_runtest_makereport(item, call):
    6. """ return a :py:class:`_pytest.runner.TestReport` object
    7. for the given :py:class:`pytest.Item` and
    8. :py:class:`_pytest.runner.CallInfo`.
    9. """
    10. '''
    11. @pytest.hookimpl(hookwrapper=True, tryfirst=True)
    12. def pytest_runtest_makereport(item, call):
    13. print('------------------------------------')
    14. # 获取钩子方法的调用结果
    15. out = yield
    16. # print('用例执行结果', out)
    17. # 3. 从钩子方法的调用结果中获取测试报告
    18. report = out.get_result()
    19. if report.when == "call":
    20. print('测试报告:%s' % report)
    21. print('步骤:%s' % report.when)
    22. print('nodeid:%s' % report.nodeid)
    23. print('description:%s' % str(item.function.__doc__))
    24. print(('运行结果: %s' % report.outcome))
    25. @pytest.fixture(scope="session", autouse=True)
    26. def fix_a():
    27. print("setup 前置操作")
    28. yield
    29. print("teardown 后置操作")

    allure报告集成错误截图 需要使用conftest.py文件,conftest.py需要存在在测试目录中,文件名不能变更,可以根据模块创建层级嵌套。

    具体参照pytest的官方文档

    1. @pytest.hookimpl(tryfirst=True, hookwrapper=True)
    2. def pytest_runtest_makereport(item, call):
    3. '''
    4. hook pytest失败
    5. :param item:
    6. :param call:
    7. :return:
    8. '''
    9. # execute all other hooks to obtain the report object
    10. outcome = yield
    11. rep = outcome.get_result()
    12. # we only look at actual failing test calls, not setup/teardown
    13. if rep.when == "call" and rep.failed:
    14. mode = "a" if os.path.exists("failures") else "w"
    15. with open("failures", mode) as f:
    16. # let's also access a fixture for the fun of it
    17. if "tmpdir" in item.fixturenames:
    18. extra = " (%s)" % item.funcargs["tmpdir"]
    19. else:
    20. extra = ""
    21. f.write(rep.nodeid + extra + "\n")
    22. # pic_info = adb_screen_shot()
    23. with allure.step('添加失败截图...'):
    24. allure.attach(driver.get_screenshot_as_png(), "失败截图", allure.attachment_type.PNG)

    好了,我们可以用在我们自己的项目里面来了。 我们可以在conftest.py里面定义:

    1. import pytest
    2. from selenium import webdriver
    3. import os
    4. import allure
    5. _driver = None
    6. @pytest.hookimpl(tryfirst=True, hookwrapper=True)
    7. def pytest_runtest_makereport(item, call):
    8. '''
    9. 获取每个用例状态的钩子函数
    10. :param item:
    11. :param call:
    12. :return:
    13. '''
    14. # 获取钩子方法的调用结果
    15. outcome = yield
    16. rep = outcome.get_result()
    17. # 仅仅获取用例call 执行结果是失败的情况, 不包含 setup/teardown
    18. if rep.when == "call" and rep.failed:
    19. mode = "a" if os.path.exists("failures") else "w"
    20. with open("failures", mode) as f:
    21. # let's also access a fixture for the fun of it
    22. if "tmpdir" in item.fixturenames:
    23. extra = " (%s)" % item.funcargs["tmpdir"]
    24. else:
    25. extra = ""
    26. f.write(rep.nodeid + extra + "\n")
    27. # 添加allure报告截图
    28. if hasattr(_driver, "get_screenshot_as_png"):
    29. with allure.step('添加失败截图...'):
    30. allure.attach(_driver.get_screenshot_as_png(), "失败截图", allure.attachment_type.PNG)
    31. @pytest.fixture(scope='session')
    32. def browser():
    33. global _driver
    34. if _driver is None:
    35. _driver =webdriver.Chrome()
    36. yield _driver
    37. print("1111111111")
    38. _driver.quit()

    然后写一个测试用例,如在某度上搜一个关键词。

    1. @allure.feature('self study')
    2. class TestLesson():
    3. @allure.story('user course page')
    4. @allure.description('be course')
    5. def test_be_ge_course(self,browser):
    6. url = 'http://www.baidu.com'
    7. browser.get(url)
    8. time.sleep(2)
    9. browser.find_element_by_id('kw').send_keys("python")
    10. with allure.step('查找元素...'):
    11. browser.find_element_by_id('su').click()
    12. time.sleep(2)
    13. assert browser.title == 'python'

    这是一个失败的用例,所以执行错误会截图。

    这样你的报告就看起来高大上了。 截图还可以直接用allure.attach allure.attach(挺有用的) 作用:allure报告还支持显示许多不同类型的附件,可以补充测试结果;自己想输出啥就输出啥,挺好的

    1. 语法:allure.attach(body, name, attachment_type, extension)
    2. 参数列表
    3. body:要显示的内容(附件)
    4. name:附件名字
    5. attachment_type:附件类型,是 allure.attachment_type 里面的其中一种
    6. extension:附件的扩展名(比较少用)

    allure.attach.file(source, name, attachment_type, extension) source:文件路径,相当于传一个文件

    其他参数和上面的一致:

    1. TEXT = ("text/plain", "txt")
    2. CSV = ("text/csv", "csv")
    3. TSV = ("text/tab-separated-values", "tsv")
    4. URI_LIST = ("text/uri-list", "uri")
    5. HTML = ("text/html", "html")
    6. XML = ("application/xml", "xml")
    7. JSON = ("application/json", "json")
    8. YAML = ("application/yaml", "yaml")
    9. PCAP = ("application/vnd.tcpdump.pcap", "pcap")
    10. PNG = ("image/png", "png")
    11. JPG = ("image/jpg", "jpg")
    12. SVG = ("image/svg-xml", "svg")
    13. GIF = ("image/gif", "gif")
    14. BMP = ("image/bmp", "bmp")
    15. TIFF = ("image/tiff", "tiff")
    16. MP4 = ("video/mp4", "mp4")
    17. OGG = ("video/ogg", "ogg")
    18. WEBM = ("video/webm", "webm")
    19. PDF = ("application/pdf", "pdf")

    根据需要,在报告里将更多的信息展现出来。 这周末啥也没干,主要就搞明白了这个。

    最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

    这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你! 

  • 相关阅读:
    设计模式-命令模式
    循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(1)
    Web基础与HTTP协议
    专利申请流程细节问答(Q & A)
    数字乡村包括哪些方面?数字乡村应用介绍
    计算机导论模拟试题一标准答案
    《未来简史》读书笔记
    100天精通Andriod逆向——第1天:ADB原理及其常用命令
    vue3+te项目正式开发
    NeuroImage:通信辅助技术削弱了脑间同步?看来维系情感还得面对面互动才行...
  • 原文地址:https://blog.csdn.net/2301_78276982/article/details/134333931