pytest使用allure测试报告详解

最近通过群友了解到了allure这个报告,开始还不以为然,但还是逃不过真香定律。

经过试用之后,发现这个报告真的很好,很适合自动化测试结果的展示。下面说说我的探索历程吧。

选用的项目为Selenium自动化测试Pytest框架实战,在这个项目的基础上说allure报告。

allure安装

首先安装python的allure-pytest包

pip install allure-pytest

然后安装allure的command命令行程序

MacOS直接使用homebrew工具执行 brew install allure 即可安装,不用配置下载包和配置环境

在GitHub下载安装程序https://github.com/allure-framework/allure2/releases

但是由于GitHub访问太慢,我已经下载好并放在了群文件里面

下载完成后解压放到一个文件夹。我的路径是D:\Program Files\allure-2.13.3

然后配置环境变量: 在系统变量path中添加D:\Program Files\allure-2.13.3\bin,然后确定保存。

打开cmd,输入allure,如果结果显示如下则表示成功了:

 
  1. C:\Users\hoou>allure

  2. Usage: allure [options] [command] [command options]

  3. Options:

  4. --help

  5. Print commandline help.

  6. -q, --quiet

  7. Switch on the quiet mode.

  8. Default: false

  9. -v, --verbose

  10. Switch on the verbose mode.

  11. Default: false

  12. --version

  13. Print commandline version.

  14. Default: false

  15. Commands:

  16. generate Generate the report

  17. Usage: generate [options] The directories with allure results

  18. Options:

  19. -c, --clean

  20. Clean Allure report directory before generating a new one.

  21. Default: false

  22. --config

  23. Allure commandline config path. If specified overrides values from

  24. --profile and --configDirectory.

  25. --configDirectory

  26. Allure commandline configurations directory. By default uses

  27. ALLURE_HOME directory.

  28. --profile

  29. Allure commandline configuration profile.

  30. -o, --report-dir, --output

  31. The directory to generate Allure report into.

  32. Default: allure-report

  33. serve Serve the report

  34. Usage: serve [options] The directories with allure results

  35. Options:

  36. --config

  37. Allure commandline config path. If specified overrides values from

  38. --profile and --configDirectory.

  39. --configDirectory

  40. Allure commandline configurations directory. By default uses

  41. ALLURE_HOME directory.

  42. -h, --host

  43. This host will be used to start web server for the report.

  44. -p, --port

  45. This port will be used to start web server for the report.

  46. Default: 0

  47. --profile

  48. Allure commandline configuration profile.

  49. open Open generated report

  50. Usage: open [options] The report directory

  51. Options:

  52. -h, --host

  53. This host will be used to start web server for the report.

  54. -p, --port

  55. This port will be used to start web server for the report.

  56. Default: 0

  57. plugin Generate the report

  58. Usage: plugin [options]

  59. Options:

  60. --config

  61. Allure commandline config path. If specified overrides values from

  62. --profile and --configDirectory.

  63. --configDirectory

  64. Allure commandline configurations directory. By default uses

  65. ALLURE_HOME directory.

  66. --profile

  67. Allure commandline configuration profile.

allure初体验

改造一下之前的测试用例代码

 
  1. #!/usr/bin/env python3

  2. # -*- coding:utf-8 -*-

  3. import re

  4. import pytest

  5. import allure

  6. from utils.logger import log

  7. from common.readconfig import ini

  8. from page_object.searchpage import SearchPage

  9. @allure.feature("测试百度模块")

  10. class TestSearch:

  11. @pytest.fixture(scope='function', autouse=True)

  12. def open_baidu(self, drivers):

  13. """打开百度"""

  14. search = SearchPage(drivers)

  15. search.get_url(ini.url)

  16. @allure.story("搜索selenium结果用例")

  17. def test_001(self, drivers):

  18. """搜索"""

  19. search = SearchPage(drivers)

  20. search.input_search("selenium")

  21. search.click_search()

  22. result = re.search(r'selenium', search.get_source)

  23. log.info(result)

  24. assert result

  25. @allure.story("测试搜索候选用例")

  26. def test_002(self, drivers):

  27. """测试搜索候选"""

  28. search = SearchPage(drivers)

  29. search.input_search("selenium")

  30. log.info(list(search.imagine))

  31. assert all(["selenium" in i for i in search.imagine])

  32. if __name__ == '__main__':

  33. pytest.main(['TestCase/test_search.py', '--alluredir', './allure'])

  34. os.system('allure serve allure')

然后运行一下:

注意:如果你使用的是pycharm编辑器,请跳过该运行方式,直接使用.bat或.sh的方式运行

 
  1. ***

  2. ------------------------------- generated html file: file://C:\Users\hoou\PycharmProjects\web-demotest\report.html --------------------------------

  3. Results (12.97s):

  4. 2 passed

  5. Generating report to temp directory...

  6. Report successfully generated to C:\Users\hoou\AppData\Local\Temp\112346119265936111\allure-report

  7. Starting web server...

  8. 2020-06-18 22:52:44.500:INFO::main: Logging initialized @1958ms to org.eclipse.jetty.util.log.StdErrLog

  9. Server started at <http://172.18.47.241:6202/>. Press <Ctrl+C> to exit

命令行会出现如上提示,接着浏览器会自动打开:

点击左下角En即可选择切换为中文。

是不是很清爽很友好,比pytest-html更舒服。

allure装饰器介绍

报告的生成和展示
刚才的两个命令:

生成allure原始报告到report/allure目录下,生成的全部为json或txt文件。

pytest TestCase/test_search.py --alluredir ./allure

在一个临时文件中生成报告并启动浏览器打开

allure serve allure

但是在关闭浏览器之后这个报告就再也打不开了。不建议使用这种。

所以我们必须使用其他的命令,让allure可以指定生成的报告目录。

我们在项目的根目录中创建run_case.py文件,内容如下:

 
  1. #!/usr/bin/env python3

  2. # -*- coding:utf-8 -*-

  3. import sys

  4. import subprocess

  5. WIN = sys.platform.startswith('win')

  6. def main():

  7. """主函数"""

  8. steps = [

  9. "venv\\Script\\activate" if WIN else "source venv/bin/activate",

  10. "pytest --alluredir allure-results --clean-alluredir",

  11. "allure generate allure-results -c -o allure-report",

  12. "allure open allure-report"

  13. ]

  14. for step in steps:

  15. subprocess.run("call " + step if WIN else step, shell=True)

  16. if __name__ == "__main__":

  17. main()

命令释义:

1、使用pytest生成原始报告,里面大多数是一些原始的json数据,加入--clean-alluredir参数清除allure-results历史数据。

pytest --alluredir allure-results --clean-alluredir

--clean-alluredir 清除allure-results历史数据

2、使用generate命令导出HTML报告到新的目录

allure generate allure-results -o allure-report

-c 在生成报告之前先清理之前的报告目录

-o 指定生成报告的文件夹

3、使用open命令在浏览器中打开HTML报告

allure open allure-report

好了我们运行一下该文件。

 
  1. Results (12.85s):

  2. 2 passed

  3. Report successfully generated to c:\Users\hoou\PycharmProjects\web-demotest\allure-report

  4. Starting web server...

  5. 2020-06-18 23:30:24.122:INFO::main: Logging initialized @260ms to org.eclipse.jetty.util.log.StdErrLog

  6. Server started at <http://172.18.47.241:7932/>. Press <Ctrl+C> to exit

可以看到运行成功了。

在项目中的allure-report文件夹也生成了相应的报告。

allure发生错误截图

上面的用例全是运行成功的,没有错误和失败的,那么发生了错误怎么样在allure报告中生成错误截图呢,我们一起来看看。

首先我们先在config/conf.py文件中添加一个截图目录和截图文件的配置。

 
  1. +++

  2. class ConfigManager(object):

  3. ...

  4. @property

  5. def screen_path(self):

  6. """截图目录"""

  7. screenshot_dir = os.path.join(self.BASE_DIR, 'screen_capture')

  8. if not os.path.exists(screenshot_dir):

  9. os.makedirs(screenshot_dir)

  10. now_time = dt_strftime("%Y%m%d%H%M%S")

  11. screen_file = os.path.join(screenshot_dir, "{}.png".format(now_time))

  12. return now_time, screen_file

  13. ...

  14. +++

然后我们修改项目目录中的conftest.py:

 
  1. #!/usr/bin/env python3

  2. # -*- coding:utf-8 -*-

  3. import base64

  4. import pytest

  5. import allure

  6. from py.xml import html

  7. from selenium import webdriver

  8. from config.conf import cm

  9. from common.readconfig import ini

  10. from utils.times import timestamp

  11. from utils.send_mail import send_report

  12. driver = None

  13. @pytest.fixture(scope='session', autouse=True)

  14. def drivers(request):

  15. global driver

  16. if driver is None:

  17. driver = webdriver.Chrome()

  18. driver.maximize_window()

  19. def fn():

  20. driver.quit()

  21. request.addfinalizer(fn)

  22. return driver

  23. @pytest.hookimpl(hookwrapper=True)

  24. def pytest_runtest_makereport(item):

  25. """

  26. 当测试失败的时候,自动截图,展示到html报告中

  27. :param item:

  28. """

  29. pytest_html = item.config.pluginmanager.getplugin('html')

  30. outcome = yield

  31. report = outcome.get_result()

  32. report.description = str(item.function.__doc__)

  33. extra = getattr(report, 'extra', [])

  34. if report.when == 'call' or report.when == "setup":

  35. xfail = hasattr(report, 'wasxfail')

  36. if (report.skipped and xfail) or (report.failed and not xfail):

  37. screen_img = _capture_screenshot()

  38. if screen_img:

  39. html = '<div><img src="data:image/png;base64,%s" alt="screenshot" style="width:1024px;height:768px;" ' \

  40. 'onclick="window.open(this.src)" align="right"/></div>' % screen_img

  41. extra.append(pytest_html.extras.html(html))

  42. report.extra = extra

  43. def pytest_html_results_table_header(cells):

  44. cells.insert(1, html.th('用例名称'))

  45. cells.insert(2, html.th('Test_nodeid'))

  46. cells.pop(2)

  47. def pytest_html_results_table_row(report, cells):

  48. cells.insert(1, html.td(report.description))

  49. cells.insert(2, html.td(report.nodeid))

  50. cells.pop(2)

  51. def pytest_html_results_table_html(report, data):

  52. if report.passed:

  53. del data[:]

  54. data.append(html.div('通过的用例未捕获日志输出.', class_='empty log'))

  55. def pytest_html_report_title(report):

  56. report.title = "pytest示例项目测试报告"

  57. def pytest_configure(config):

  58. config._metadata.clear()

  59. config._metadata['测试项目'] = "测试百度官网搜索"

  60. config._metadata['测试地址'] = ini.url

  61. def pytest_html_results_summary(prefix, summary, postfix):

  62. # prefix.clear() # 清空summary中的内容

  63. prefix.extend([html.p("所属部门: XX公司测试部")])

  64. prefix.extend([html.p("测试执行人: 随风挥手")])

  65. def pytest_terminal_summary(terminalreporter, exitstatus, config):

  66. """收集测试结果"""

  67. result = {

  68. "total": terminalreporter._numcollected,

  69. 'passed': len(terminalreporter.stats.get('passed', [])),

  70. 'failed': len(terminalreporter.stats.get('failed', [])),

  71. 'error': len(terminalreporter.stats.get('error', [])),

  72. 'skipped': len(terminalreporter.stats.get('skipped', [])),

  73. # terminalreporter._sessionstarttime 会话开始时间

  74. 'total times': timestamp() - terminalreporter._sessionstarttime

  75. }

  76. print(result)

  77. if result['failed'] or result['error']:

  78. send_report()

  79. def _capture_screenshot():

  80. """截图保存为base64"""

  81. now_time, screen_file = cm.screen_path

  82. driver.save_screenshot(screen_file)

  83. allure.attach.file(screen_file,

  84. "失败截图{}".format(now_time),

  85. allure.attachment_type.PNG)

  86. with open(screen_file, 'rb') as f:

  87. imagebase64 = base64.b64encode(f.read())

  88. return imagebase64.decode()

来看看我们修改了什么:

我们修改了_capture_screenshot函数

在里面我们使用了webdriver截图生成文件,并使用allure.attach.file方法将文件添加到了allure测试报告中。

并且我们还返回了图片的base64编码,这样可以让pytest-html的错误截图和allure都能生效。

运行一次得到两份报告,一份是简单的一份是好看内容丰富的。

现在我们在测试用例中构建一个预期的错误测试一个我们的这个代码。

修改test_002测试用例

        assert not all(["selenium" in i for i in search.imagine])

运行一下:

可以看到allure报告中已经有了这个错误的信息。

再来看看pytest-html中生成的报告:

可以看到两份生成的报告都附带了错误的截图,真是鱼和熊掌可以兼得呢。

好了,到这里可以说allure的报告就先到这里了,以后发现allure其他的精彩之处我再来分享。

 

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

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!有需要的小伙伴可以点击下方小卡片领取   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值