Python+Selenium自动化测试报告生成实战:从pytest-html到邮件发送
1. 项目概述为什么我们需要一份“会说话”的测试报告如果你已经用 Python 和 Selenium 写了不少自动化测试脚本每天定时跑看着控制台里一行行PASS或FAIL的输出是不是觉得任务已经完成了但当你需要向项目经理、产品经理或者测试负责人汇报测试结果时问题就来了。你总不能把黑乎乎的控制台日志截图发过去然后说“看有3个用例失败了。”对方大概率会一头雾水失败在哪一步当时页面长什么样是偶发问题还是必现缺陷对整个版本的质量影响有多大这就是自动化测试报告存在的核心价值。一份好的测试报告不仅仅是测试结果的“记录员”更是质量状况的“翻译官”和“分析师”。它需要将冰冷的执行日志转化为直观、可读、甚至可交互的质量视图。Python Selenium 的组合在驱动浏览器完成各种复杂操作上已经非常强大但如何将这一过程的“证据”和“结论”优雅地呈现出来是提升自动化工程价值的关键一步。我见过很多团队的自动化脚本写得不错但报告环节却草草了事导致自动化成果的能见度和影响力大打折扣。本文将从一个资深测试开发的角度手把手带你搭建一个不仅能用而且好用、专业的自动化测试报告生成体系。我们会超越简单的print语句和unittest的文本输出深入探讨如何生成结构清晰的 HTML 报告、如何集成截图与日志、如何定制化报告内容以及如何让报告自动发送到相关方手中。整个过程我会穿插我趟过的坑和总结的最佳实践目标是让你生成的每一份报告都能清晰、有力地为产品质量代言。2. 测试报告的核心诉求与方案选型在动手写代码之前我们必须想清楚一份理想的自动化测试报告到底应该包含哪些要素这决定了我们技术方案的选择。2.1 一份优秀测试报告的必备要素根据我多年的经验一份能拿得出手的自动化测试报告至少需要满足以下几点结果一目了然总体通过率、用例总数、成功数、失败数、错误数、耗时等核心摘要必须在报告最显眼的位置展示。最好能用图表如饼图、柱状图直观呈现。详情可追溯对于每一个测试用例无论是成功还是失败报告都应提供详细信息包括用例名称、描述、执行耗时。对于失败的用例这是重中之重。失败现场还原这是 Selenium UI 自动化报告的灵魂。当用例失败时报告必须能附上失败瞬间的页面截图。更进一步如果能提供失败时的错误堆栈信息将极大提升排查效率。日志集成测试执行过程中的关键操作日志如“点击登录按钮”、“输入用户名XXX”、“断言首页标题”应该能嵌入到报告中形成完整的操作流水线方便复现问题。格式友好与可分发HTML 格式是首选因为它跨平台、易浏览、样式美观。报告最好能通过邮件等方式自动发送给项目组成员。2.2 Python 测试报告方案对比围绕这些诉求Python 生态中有几个主流的选择我们来分析一下各自的优劣方案一unittest HTMLTestRunner这是最经典、资料最多的组合。HTMLTestRunner是一个第三方库专门为unittest框架生成 HTML 报告。优点简单直接与 Python 标准库unittest无缝集成生成的报告基础信息完整。缺点原版HTMLTestRunner样式较为古老且功能扩展性一般比如自定义截图需要额外处理。社区有多个改良版但需要甄选。方案二pytest pytest-htmlpytest是当前 Python 测试领域的事实标准功能强大插件生态丰富。pytest-html是官方推荐的 HTML 报告生成插件。优点与pytest深度集成使用极其简单一个命令行参数即可。支持丰富的钩子函数可以非常方便地添加截图、额外信息等。报告样式现代支持排序、过滤。缺点默认报告更偏向于通用测试对 UI 自动化特有的“失败截图”等需求需要一定的配置才能完美实现。方案三Allure 框架Allure 是一个轻量级、多语言的测试报告工具能生成非常精美、交互性极强的报告。优点报告颜值和交互体验顶级支持用例分层、附件截图、日志、文本、步骤Step展示功能非常强大。缺点需要额外安装 Java 环境Allure 命令行工具集成步骤比前两者稍显复杂属于“重型”但专业的解决方案。我的选择与建议对于刚入门或希望快速搭建可靠报告体系的同学我强烈推荐方案二pytest pytest-html。它平衡了易用性、美观度和扩展性。pytest本身比unittest更灵活强大pytest-html的扩展能力足以满足我们定制化报告的需求。本文将主要基于此方案展开同时也会介绍如何通过HTMLTestRunner满足一些特定场景。Allure 更适合追求极致报告体验和已有一定基础的中大型项目。注意无论选择哪种方案切记不要只停留在生成报告本身。一定要把失败截图和日志记录作为报告不可分割的一部分来设计和实现。没有现场证据的报告价值折半。3. 基础环境搭建与项目结构设计工欲善其事必先利其器。我们先来搭建一个清晰、可维护的自动化测试项目环境。3.1 环境准备与依赖安装首先确保你已安装 Python建议 3.8 及以上版本。然后我们通过pip安装核心库。# 安装 Selenium WebDriver用于浏览器自动化 pip install selenium # 安装 pytest 测试框架及其 HTML 报告插件 pip install pytest pytest-html # 安装 WebDriver 管理器自动管理浏览器驱动强烈推荐避免手动下载驱动版本不匹配的噩梦 pip install webdriver-manager为什么选择webdriver-manager以前做 Selenium 项目最头疼的就是 ChromeDriver 或 GeckoDriver 的版本与本地浏览器版本匹配问题。webdriver-manager这个库能自动检测你系统安装的浏览器版本并下载匹配的驱动文件极大降低了环境配置的复杂度。3.2 项目目录结构规划一个合理的目录结构是项目可维护性的基石。不要把所有文件都扔在一个文件夹里。我建议的目录结构如下your_autotest_project/ ├── configs/ # 配置文件目录 │ ├── __init__.py │ └── settings.py # 存放URL、账号、超时时间等全局配置 ├── pages/ # 页面对象模型Page Object目录 │ ├── __init__.py │ ├── base_page.py # 页面基类封装公共方法 │ └── login_page.py # 示例登录页面对象 ├── test_cases/ # 测试用例目录 │ ├── __init__.py │ ├── conftest.py # pytest 共享 fixture 配置如驱动初始化 │ └── test_login.py # 示例登录测试用例 ├── reports/ # 测试报告输出目录.gitignore中应忽略 │ └── assets/ # 报告附带的截图等资源 ├── logs/ # 日志文件目录.gitignore中应忽略 ├── utils/ # 工具函数目录 │ ├── __init__.py │ ├── logger.py # 日志记录器模块 │ └── screenshot.py # 截图工具模块 ├── requirements.txt # 项目依赖列表 └── run_tests.py # 测试执行入口脚本这个结构体现了“关注点分离”的思想。pages负责页面元素和操作test_cases负责测试逻辑utils负责通用工具configs负责配置管理。conftest.py是pytest的魔力所在我们可以在其中定义全局的fixture比如初始化和退出 WebDriver。4. 核心实现使用 pytest-html 生成增强型报告现在我们进入核心环节一步步实现一个能生成带截图和日志的 HTML 报告的系统。4.1 编写 conftest.py 实现驱动管理与失败截图conftest.py是pytest的本地插件文件其中定义的fixture可以被同一目录及子目录下的所有测试文件使用。我们将在这里设置 WebDriver 的生命周期和关键的失败截图钩子。# test_cases/conftest.py import pytest from selenium import webdriver from selenium.webdriver.chrome.service import Service as ChromeService from webdriver_manager.chrome import ChromeDriverManager from datetime import datetime import os # 定义一个 fixture用于创建和退出 WebDriver pytest.fixture(scopefunction) # 每个测试函数执行一次 def driver(request): # request 是 pytest 内置的 fixture用于访问测试上下文 # 使用 webdriver-manager 自动管理 ChromeDriver options webdriver.ChromeOptions() options.add_argument(--headless) # 无头模式不打开浏览器UI适合CI环境。调试时可注释掉。 options.add_argument(--disable-gpu) options.add_argument(--no-sandbox) options.add_argument(--window-size1920,1080) driver webdriver.Chrome(serviceChromeService(ChromeDriverManager().install()), optionsoptions) driver.implicitly_wait(10) # 设置隐式等待时间 # 将 driver 对象添加到测试请求中方便其他 fixture 或测试用例访问 request.cls.driver driver if hasattr(request, cls) else None yield driver # 测试函数执行时使用此 driver # 测试函数执行完毕后执行清理工作 driver.quit() # 最重要的部分pytest-html 钩子用于在报告中添加额外内容这里我们添加失败截图 pytest.hookimpl(hookwrapperTrue) def pytest_runtest_makereport(item, call): 这个钩子函数会在每个测试步骤setup, call, teardown生成报告时被调用。 我们主要关注 call 阶段即测试执行本身且测试失败的情况。 outcome yield # 先执行默认的 report 生成逻辑 report outcome.get_result() # 获取测试报告对象 # 我们只关心测试执行阶段call且测试失败或错误的情况 if report.when call and report.failed: # 尝试从测试用例中获取 driver 对象 driver_fixture item.funcargs.get(driver) if driver_fixture is not None: # 生成唯一的截图文件名 timestamp datetime.now().strftime(%Y%m%d_%H%M%S) screenshot_name f{item.name}_{timestamp}.png # 指定截图保存路径放在 reports/assets 下 reports_dir os.path.join(os.path.dirname(__file__), .., reports) assets_dir os.path.join(reports_dir, assets) os.makedirs(assets_dir, exist_okTrue) # 确保目录存在 screenshot_path os.path.join(assets_dir, screenshot_name) try: driver_fixture.save_screenshot(screenshot_path) # 将截图路径作为额外信息添加到 html 报告中 # 这里将截图以 base64 格式嵌入这样报告是单个HTML文件便于传输 with open(screenshot_path, rb) as f: screenshot_data f.read() import base64 html fdivimg srcdata:image/png;base64,{base64.b64encode(screenshot_data).decode()} alt失败截图 stylewidth:80%; border:1px solid #ccc; onclickwindow.open(this.src) aligncenter//div # 将这段HTML添加到报告的 extra 字段中 if hasattr(report, extra): # 注意pytest-html 的 extra 格式 from pytest_html import extras report.extra [extras.html(html)] except Exception as e: print(f截图失败: {e})关键点解析driverfixture (scope”function”): 这确保了每个测试用例都会获得一个全新的浏览器实例用例之间相互隔离避免了状态污染。pytest_runtest_makereport钩子: 这是实现失败截图自动附加到报告的核心。它拦截测试报告生成过程在测试失败时获取当前测试用例使用的driver对象然后调用save_screenshot方法。我们将截图以 Base64 格式直接嵌入 HTML生成的是单个文件报告分享起来非常方便。无头模式 (–headless)在持续集成CI服务器上运行时通常没有图形界面必须使用无头模式。本地调试时可以注释掉这行方便观察浏览器操作。4.2 编写页面对象与测试用例为了演示我们创建一个简单的登录页面测试。首先实现一个页面对象。# pages/base_page.py from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import TimeoutException import logging class BasePage: def __init__(self, driver): self.driver driver self.logger logging.getLogger(__name__) def find_element(self, locator, timeout10): 查找单个元素加入显式等待 try: element WebDriverWait(self.driver, timeout).until( EC.presence_of_element_located(locator) ) self.logger.info(f找到元素: {locator}) return element except TimeoutException: self.logger.error(f查找元素超时: {locator}) raise def click(self, locator): 点击元素 element self.find_element(locator) element.click() self.logger.info(f点击元素: {locator}) def input_text(self, locator, text): 输入文本 element self.find_element(locator) element.clear() element.send_keys(text) self.logger.info(f在元素 {locator} 中输入文本: {text}) # pages/login_page.py from selenium.webdriver.common.by import By from .base_page import BasePage class LoginPage(BasePage): # 定位器 USERNAME_INPUT (By.ID, username) PASSWORD_INPUT (By.ID, password) LOGIN_BUTTON (By.ID, loginBtn) ERROR_MSG (By.CLASS_NAME, error-message) def __init__(self, driver): super().__init__(driver) self.driver driver def login(self, username, password): 登录操作 self.input_text(self.USERNAME_INPUT, username) self.input_text(self.PASSWORD_INPUT, password) self.click(self.LOGIN_BUTTON) def get_error_message(self): 获取错误提示信息 try: element self.find_element(self.ERROR_MSG, timeout3) return element.text except TimeoutException: return None接下来编写一个使用pytest和上述页面对象的测试用例。# test_cases/test_login.py import pytest import logging from pages.login_page import LoginPage # 一个简单的测试数据实际项目中可能从文件或数据库读取 TEST_DATA [ (correct_user, correct_pass, True, 登录成功), (wrong_user, wrong_pass, False, 用户名或密码错误), ] class TestLogin: 登录功能测试类 pytest.mark.parametrize(username, password, expected_success, expected_msg, TEST_DATA) def test_user_login(self, driver, username, password, expected_success, expected_msg): 测试用户登录功能 :param driver: 来自 conftest.py 的 fixture :param username: 用户名 :param password: 密码 :param expected_success: 预期是否成功登录 :param expected_msg: 预期消息成功或错误提示 # 初始化日志记录器 logger logging.getLogger(__name__) logger.info(f开始执行测试用例: test_user_login - 用户名: {username}) # 访问测试登录页这里用一个模拟的在线测试页实际项目替换为你的地址 driver.get(https://example.com/login) # 请替换为实际的测试URL login_page LoginPage(driver) # 执行登录操作 login_page.login(username, password) # 根据预期结果进行断言 if expected_success: # 预期成功验证是否跳转到首页这里假设首页标题包含Dashboard logger.info(预期登录成功验证页面跳转。) assert Dashboard in driver.title, f登录成功后未跳转到正确页面当前标题: {driver.title} logger.info(登录成功断言通过。) else: # 预期失败验证错误提示信息是否正确 logger.info(预期登录失败验证错误提示。) actual_error login_page.get_error_message() assert actual_error is not None, 未找到错误提示信息 assert expected_msg in actual_error, f错误提示不匹配。预期包含 {expected_msg}实际为 {actual_error} logger.info(登录失败断言通过。) logger.info(f测试用例 test_user_login - 用户名: {username} 执行完毕。)4.3 配置日志系统为了让报告中的操作有迹可循一个健壮的日志系统必不可少。我们创建一个工具类来统一管理日志。# utils/logger.py import logging import os from datetime import datetime def setup_logger(name__name__, log_levellogging.INFO): 配置并返回一个日志记录器。 将日志同时输出到控制台和文件。 # 创建日志记录器 logger logging.getLogger(name) logger.setLevel(log_level) # 避免重复添加处理器 if logger.handlers: return logger # 定义日志格式 formatter logging.Formatter(%(asctime)s - %(name)s - %(levelname)s - %(message)s) # 控制台处理器 console_handler logging.StreamHandler() console_handler.setLevel(log_level) console_handler.setFormatter(formatter) logger.addHandler(console_handler) # 文件处理器 - 按日期生成日志文件 log_dir os.path.join(os.path.dirname(__file__), .., logs) os.makedirs(log_dir, exist_okTrue) log_file os.path.join(log_dir, fautotest_{datetime.now().strftime(%Y%m%d)}.log) file_handler logging.FileHandler(log_file, encodingutf-8) file_handler.setLevel(log_level) file_handler.setFormatter(formatter) logger.addHandler(file_handler) return logger # 在 conftest.py 或测试用例开头初始化一个全局日志器 # 例如在 conftest.py 顶部添加 # import sys # sys.path.append(os.path.join(os.path.dirname(__file__), ..)) # from utils.logger import setup_logger # logger setup_logger()4.4 执行测试并生成报告一切就绪后我们可以通过命令行或脚本执行测试并生成报告。方式一使用命令行最常用在项目根目录下打开终端执行# 运行 test_cases 目录下所有测试并生成HTML报告到 reports 目录 pytest test_cases/ -v --htmlreports/report.html --self-contained-html-v: 显示详细输出。--htmlreports/report.html: 指定 HTML 报告生成路径。--self-contained-html: 将 CSS 和图片如我们的 Base64 截图全部嵌入到一个 HTML 文件中生成独立的报告文件。强烈建议加上此参数方便报告传输。方式二编写执行脚本创建一个run_tests.py文件便于一键执行和后续集成到 CI/CD。# run_tests.py import subprocess import sys from datetime import datetime def run_tests(): 执行测试并生成报告 # 生成带时间戳的报告文件名避免覆盖 timestamp datetime.now().strftime(%Y%m%d_%H%M%S) report_name freports/test_report_{timestamp}.html # 构建 pytest 命令 # 这里添加了 --capturesys 可以更好地捕获日志--tbshort 使错误回溯更简洁 command [ sys.executable, -m, pytest, test_cases/, -v, f--html{report_name}, --self-contained-html, --capturesys, --tbshort ] print(f开始执行测试报告将生成至: {report_name}) print(命令:, .join(command)) # 执行命令 result subprocess.run(command) # 根据 pytest 退出码判断测试结果 if result.returncode 0: print(所有测试用例通过) elif result.returncode 1: print(部分测试用例失败请查看报告。) else: print(测试执行过程出现错误。) print(f报告已生成: {report_name}) return result.returncode if __name__ __main__: sys.exit(run_tests())运行python run_tests.py即可。生成的report.html用浏览器打开你会看到一个清晰的测试摘要、详细的用例列表并且失败的用例下方会直接显示当时的页面截图5. 报告增强与定制化技巧基础报告生成后我们可以根据项目需求进行深度定制让它更加强大。5.1 在报告中添加自定义内容如环境信息我们可以在conftest.py中利用pytest的另一个钩子pytest_configure来修改报告的元数据。# 在 conftest.py 中添加 def pytest_configure(config): 在测试开始前配置用于向报告添加元数据 # 这些信息会显示在报告的“Environment”部分 config._metadata { 项目名称: Web自动化测试项目, 测试环境: Staging, Python版本: 3.9, 浏览器: Chrome (Headless), 执行人: 自动化测试平台 } def pytest_html_report_title(report): 修改HTML报告的标题 report.title 自动化测试执行报告 def pytest_html_results_summary(prefix, summary, postfix): 在报告摘要部分添加自定义内容 # 例如可以在这里添加一个指向日志文件的链接需要先将日志文件作为附件 # 这部分需要更复杂的处理例如将日志文件也以extra形式添加 pass5.2 处理并展示测试日志默认情况下pytest捕获的日志不会直接显示在pytest-html报告中。为了让操作日志与测试步骤关联我们可以将关键日志也作为“额外信息”添加到报告中。这需要对之前的pytest_runtest_makereport钩子进行增强或者使用pytest的caplogfixture 来捕获特定日志。一个更实用的方法是在测试用例中通过report.extra添加重要的、自定义的日志信息。例如在关键断言或操作后添加一条描述性的 HTML。# 在 test_login.py 的测试方法中可以这样添加额外信息需要导入 extras import pytest_html def test_something(driver): # ... 一些操作 ... # 添加一个自定义的文本块到报告 extra_html pytest_html.extras.html(div stylecolor: blue; padding: 5px;关键检查点用户信息加载完成。/div) # 如何附加到报告这通常需要在钩子中处理比较繁琐。更常见的做法是依赖完善的日志文件。在报告中注明日志文件的路径或名称测试者可以结合 HTML 报告和详细的日志文件进行问题排查。我们可以通过修改pytest_configure中的元数据将日志文件路径加入环境信息。5.3 集成邮件发送功能报告生成后自动发送给相关人员是提升效率的最后一环。我们可以编写一个邮件发送模块。# utils/email_sender.py import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.mime.application import MIMEApplication import os def send_email_report(report_file_path, subject_prefix自动化测试报告): 发送测试报告邮件 :param report_file_path: 生成的HTML报告文件路径 :param subject_prefix: 邮件主题前缀 # 邮件服务器配置以QQ邮箱为例请替换为你自己的配置 smtp_server smtp.qq.com smtp_port 465 sender_email your_emailqq.com sender_password your_authorization_code # 注意是授权码不是登录密码 receiver_emails [team_leadcompany.com, testercompany.com] # 构建邮件 msg MIMEMultipart() msg[From] sender_email msg[To] , .join(receiver_emails) from datetime import datetime subject f{subject_prefix} - {datetime.now().strftime(%Y-%m-%d %H:%M)} msg[Subject] subject # 邮件正文 body f 您好 本次自动化测试执行已完成。 详细报告请查看附件或直接下载后使用浏览器打开。 报告生成时间{datetime.now().strftime(%Y-%m-%d %H:%M:%S)} msg.attach(MIMEText(body, plain, utf-8)) # 附加报告文件 with open(report_file_path, rb) as f: report_attachment MIMEApplication(f.read(), _subtypehtml) report_attachment.add_header(Content-Disposition, attachment, filenameos.path.basename(report_file_path)) msg.attach(report_attachment) # 发送邮件 try: with smtplib.SMTP_SSL(smtp_server, smtp_port) as server: server.login(sender_email, sender_password) server.sendmail(sender_email, receiver_emails, msg.as_string()) print(f测试报告邮件发送成功至 {receiver_emails}) except Exception as e: print(f邮件发送失败: {e}) # 在 run_tests.py 的 run_tests 函数末尾调用 # from utils.email_sender import send_email_report # if report_name: # send_email_report(report_name)重要安全提示切勿将邮箱密码明文写在代码中在实际项目中应使用环境变量或配置管理工具来存储敏感信息。6. 常见问题排查与实战心得即使按照步骤操作你也可能会遇到一些坑。这里我总结了一些高频问题和解决方案。6.1 问题排查速查表问题现象可能原因解决方案运行测试时提示ModuleNotFoundError: No module named ‘pytest_html’pytest-html未安装或安装环境不对。确认在正确的 Python 环境下执行pip install pytest-html。在 IDE 中运行时检查项目解释器是否选对。生成的 HTML 报告打开后样式混乱或截图不显示生成报告时未使用--self-contained-html参数导致报告依赖外部 CSS 文件。确保生成命令中包含--self-contained-html参数。如果已包含检查截图生成路径和 Base64 编码过程是否出错。失败用例没有自动截图1.conftest.py中的钩子函数未正确编写或未被加载。2. 测试失败发生在setup或teardown阶段而不是call阶段。3.driverfixture 没有成功传递给测试用例。1. 检查conftest.py文件是否放在test_cases目录下且命名正确。2. 在钩子函数中可以尝试将if report.when “call”条件改为if report.failed以捕获所有阶段的失败。3. 确保测试用例函数参数中包含了driver。报告中的截图非常小看不清截图时浏览器窗口尺寸太小。在driverfixture 中设置浏览器窗口大小如options.add_argument(‘–window-size1920,1080’)。无头模式下必须设置。日志没有输出到文件或控制台日志记录器配置错误或级别设置过高。检查utils/logger.py中的路径和级别设置。确保在测试开始时调用了setup_logger()。可以在测试开头加print(logging.getLogger().handlers)调试。邮件发送失败报 SSL 或认证错误1. 邮箱 SMTP 服务未开启。2. 使用了登录密码而非授权码。3. 防火墙或网络限制。1. 登录网页邮箱在设置中开启 SMTP/IMAP 服务。2. 生成并使用专属的授权码。3. 尝试更换端口如 587 配合starttls或检查网络。6.2 实战心得与进阶建议Fixture 的作用域管理本文的driverfixture 作用域是function每个用例都重启浏览器保证了独立性但牺牲了速度。如果用例间无状态依赖且需要提速可以尝试scope”class”每个测试类一次或scope”session”整个测试会话一次但务必在teardown时做好清理如清除 cookies。等待策略是 UI 自动化的核心除了隐式等待显式等待WebDriverWait更为可靠。将核心的等待逻辑封装在BasePage的find_element方法中是很好的实践。避免使用time.sleep()它不可靠且低效。报告的可读性pytest-html报告默认按执行顺序排列用例。可以通过pytest的-k参数选择用例或用pytest.mark给用例打标签然后按标签运行和分组展示。与 CI/CD 集成将run_tests.py脚本集成到 Jenkins、GitLab CI、GitHub Actions 等 CI/CD 流水线中。在流水线配置中安装依赖、执行脚本并将生成的reports/目录作为产物保存下来供后续查看。邮件发送功能也可以在流水线中触发。多浏览器支持可以通过定义不同的 fixture如chrome_driver,firefox_driver并配合pytest.mark.parametrize或pytest的addoption钩子来实现跨浏览器测试并在报告环境信息中注明浏览器类型。处理弹窗和新窗口在BasePage中增加处理alert、confirm、prompt以及多窗口切换的工具方法并在操作后及时回到主窗口避免后续元素定位失败。自动化测试报告的生成不是终点而是将自动化价值可视化的起点。一个稳定、清晰、信息丰富的报告能让你和你的团队对产品质量有更准确的把握也能让自动化测试工作获得更多的认可。从今天开始别再满足于控制台的绿色PASS动手打造一份属于你自己的、专业的测试报告吧。