ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Selenium自动化测试进阶:精准验证Web动态折线图数据与交互

Selenium自动化测试进阶:精准验证Web动态折线图数据与交互

1. 项目概述:当Selenium遇上折线图

如果你做过Web自动化测试,肯定对Selenium不陌生,用它来点点按钮、填填表单、校验下文本,算是家常便饭。但最近我在一个数据可视化项目的测试中,遇到了一个新需求:自动化验证一个动态生成的折线图。这听起来有点意思,对吧?不是简单地检查一个静态图片是否存在,而是要验证图表的数据点、坐标轴、提示框(Tooltip)甚至交互效果是否正确。这恰恰是很多数据报表、监控大屏类Web应用测试的痛点。手动去一个个坐标点核对?效率太低且容易出错。用传统的截图对比?动态数据每次渲染都可能微调像素,误报率太高。所以,这个“Selenium折线图自动化测试”的项目,就是为了解决如何用代码智能地“看懂”并验证一个Web折线图。

这个项目适合谁呢?首先是测试工程师,尤其是专注Web UI自动化和数据产品测试的同学。其次是前端开发,如果你需要为自己开发的ECharts、Highcharts等图表组件编写自动化验收用例,这套方法能直接集成到你的CI流程里。最后,对数据分析师或产品经理也可能有启发,你可以用类似的思路,写个小脚本自动抓取图表数据进行分析,省去手动录的麻烦。核心要解决的就是三个问题:第一,如何从渲染好的折线图里把数据“挖”出来;第二,如何验证这些数据与预期是否匹配;第三,如何模拟用户与图表的交互(比如悬停看Tooltip)并进行断言。整个过程,我们会用到Selenium进行浏览器驱动,结合JavaScript注入来与图表库“对话”,最终实现一套稳定、可复用的验证方案。

2. 核心思路与技术选型解析

2.1 为什么不用简单的截图对比?

提到UI验证,很多人的第一反应是截图对比。对于折线图,这恰恰是最容易踩坑的地方。首先,折线图的数据往往是动态的,比如实时股价、每日活跃用户数,每次测试运行时生成的数据点位置(像素坐标)很可能不同。即使数据相同,不同的浏览器、不同的缩放比例、甚至操作系统字体渲染的细微差别,都可能导致像素级差异,产生大量误报。其次,截图对比无法验证数据的正确性。图表可能渲染得很“漂亮”,但把数据标错了,比如Y轴刻度值不对,截图是看不出来的。最后,我们还需要验证交互逻辑,比如鼠标悬停在某个数据点上,是否显示了正确的Tooltip信息。这些动态行为,静态截图完全无能为力。因此,我们必须深入到图表内部,去获取其背后的数据模型和状态。

2.2 与图表库“对话”:基于JavaScript的数据提取方案

现代Web折线图大多由前端库如ECharts、Chart.js、Highcharts等渲染。这些库在浏览器内存中维护着一个完整的图表实例对象,里面包含了所有的配置项、数据序列、计算后的坐标等信息。我们的核心思路就是:通过Selenium执行JavaScript代码,直接访问这个实例对象,从中提取我们需要验证的数据。这相当于绕过了视觉层,直击数据层。这种方法有几个显著优势:一是精准,获取的是原始数据或计算后的逻辑值,不受渲染影响;二是高效,一次JS执行就能拿到大量结构化数据;三是能力强,不仅能取数据,还能调用图表实例的方法来模拟交互。

以最常用的ECharts为例,它通常通过echarts.init初始化一个图表实例,并挂载在全局变量或某个DOM元素的__echarts_instance__属性上。我们的Selenium脚本可以通过driver.execute_script方法,注入一段JS代码,找到这个实例,然后调用其getOption()方法,就能拿到包含所有系列(series)数据、坐标轴配置的完整选项对象。有了这个对象,验证工作就变成了纯粹的数据比对。

注意:这种方法强依赖于图表库的API稳定性。如果库版本升级导致API变更,测试脚本可能需要同步调整。因此,在脚本中最好对关键API调用进行兼容性判断或封装。

2.3 技术栈与工具选型

  1. Selenium WebDriver:这是我们的核心驱动引擎。选择它是因为其跨浏览器支持(Chrome, Firefox, Edge等)和成熟的社区生态。对于折线图测试,浏览器选择上我推荐Chrome,因为其开发者工具对Canvas/SVG渲染内容的调试支持最好,方便我们编写和调试提取数据的JS片段。
  2. 编程语言:Python或Java皆可。Python的语法简洁,适合快速原型开发和脚本编写,且Selenium的Python绑定非常友好。Java则在大型企业级测试框架中更常见,性能稳定。本文将以Python为例进行演示,因其受众更广,易于理解。
  3. 图表库特定知识:你需要对你所测试的图表库有基本了解。主要是知道如何获取其图表实例,以及实例上有哪些有用的API。例如:
    • EChartsecharts.getInstanceByDom(domElement)domElement.__echarts_instance__
    • Chart.jsChart.getChart(canvasElement)
    • HighchartsHighcharts.charts数组或通过SVG元素的特定属性查找。
  4. 断言库:Python推荐使用内置的unittest或第三方pytestpytest的断言更智能,错误信息更清晰。我们将用它来对比提取出的数据与预期值。
  5. 辅助工具:浏览器开发者工具(F12)是必不可少的。你需要用它来查看图表对应的DOM元素,并尝试在Console中执行数据提取的JS代码,确保可行后再移植到Selenium脚本中。

3. 实战环境搭建与基础准备

3.1 Selenium与浏览器驱动安装

首先,确保你的环境已经就绪。如果你使用Python,可以通过pip安装Selenium:

pip install selenium

接下来是浏览器驱动。以Chrome为例,你需要下载与本地Chrome浏览器版本匹配的chromedriver。可以去淘宝的NPM镜像站或者官方仓库下载。下载后,将可执行文件放在系统PATH路径下,或者直接在代码中指定其路径。

一个简单的启动脚本如下:

from selenium import webdriver from selenium.webdriver.chrome.service import Service # 指定 chromedriver 路径(如果不在PATH中) service = Service(executable_path='/path/to/your/chromedriver') driver = webdriver.Chrome(service=service) # 打开目标页面(这里以一个ECharts官方示例为例) driver.get('https://echarts.apache.org/examples/zh/editor.html?c=line-simple') driver.maximize_window() # 最大化窗口,确保图表完全显示

3.2 定位图表DOM元素

在能提取数据之前,我们必须先找到图表所在的HTML元素。折线图通常渲染在<canvas><svg>标签内。你需要使用开发者工具,检查图表区域,找到这个特定的元素。

  • 对于ECharts:它通常在一个具有固定idclass<div>容器中初始化,图表本身可能渲染为<canvas>。你需要定位到这个容器<div>
  • 对于纯Canvas/SVG:直接定位到<canvas><svg>元素。

在Selenium中,你可以使用各种定位方式:

# 假设图表在一个 id 为 ‘chart-container’ 的 div 中 chart_container = driver.find_element(By.ID, 'chart-container') # 或者通过 class 定位 # chart_container = driver.find_element(By.CLASS_NAME, 'echarts-dom')

确保你能成功定位到这个元素,这是后续所有操作的基础。有时图表是动态加载的,可能需要添加显式等待(WebDriverWait)来确保元素加载完成。

from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC wait = WebDriverWait(driver, 10) chart_container = wait.until(EC.presence_of_element_located((By.ID, 'chart-container')))

3.3 编写通用的数据提取函数

由于我们需要频繁地执行JavaScript来获取数据,封装一个通用的函数是明智之举。这个函数接收Selenium的WebElement(图表容器),然后注入JS代码来获取图表实例和选项。

下面是一个针对ECharts的通用提取函数示例:

def get_echarts_option(driver, chart_element): """ 获取ECharts图表的配置项对象。 :param driver: Selenium WebDriver 实例 :param chart_element: 图表所在的DOM元素 (WebElement) :return: 图表的option字典,如果失败则返回None """ js_code = """ // 通过DOM元素获取ECharts实例 var chartDom = arguments[0]; var chartInstance = echarts.getInstanceByDom(chartDom); if (!chartInstance) { // 另一种获取实例的方式 chartInstance = chartDom.__echarts_instance__; } if (chartInstance) { return chartInstance.getOption(); } else { console.error('未找到ECharts实例'); return null; } """ option = driver.execute_script(js_code, chart_element) return option

这个函数的核心是driver.execute_script(js_code, chart_element)arguments[0]对应我们传入的chart_element。JS代码尝试两种方式获取ECharts实例,然后调用getOption()

实操心得:在实际项目中,页面可能引入多个ECharts实例,或者实例的获取方式有差异。务必在开发者工具的Console中预先测试你的JS代码片段,确保它能准确返回你需要的数据。此外,execute_script返回的数据是JSON可序列化的,复杂的JS对象(如函数)会被忽略,通常返回的是字典/列表结构,这正合我们意。

4. 折线图核心数据验证点详解

拿到图表的option对象后,我们就拥有了验证的一切基础。一个典型的折线图option结构如下(以ECharts为例),我们需要从中提取关键信息进行断言。

4.1 验证数据序列(Series)的正确性

这是最核心的验证。在option['series']列表里,每个对象代表一条线。我们需要验证:

  1. 数据点(data):确认折线上的每个点的数值是否正确。data可能是一个数值数组(如[120, 132, 101, 134, 90, 230, 210]),也可能是包含名称和值的对象数组(如[{'name':'周一','value':120}, ...])。
  2. 序列类型(type):确保它是'line'
  3. 序列名称(name):多条线时,用于区分。

验证示例代码:

def test_line_chart_data(self): # ... 前置代码:打开页面,定位元素 ... option = get_echarts_option(self.driver, chart_container) # 1. 验证至少有一个序列 assert len(option['series']) > 0, "图表中未找到任何数据序列" # 2. 验证第一个序列是折线图 first_series = option['series'][0] assert first_series['type'] == 'line', f"序列类型错误,期望'line',实际是'{first_series['type']}'" # 3. 验证数据点(假设我们预期前三个数据点是 [120, 132, 101]) expected_data = [120, 132, 101] actual_data = first_series['data'][:3] # 取前三个点进行验证 assert actual_data == expected_data, f"数据点不匹配。期望{expected_data},实际{actual_data}" # 4. 验证序列名称(如果有) if 'name' in first_series: assert first_series['name'] == '销量', f"序列名称错误,期望'销量',实际'{first_series['name']}'"

4.2 验证坐标轴(Axis)配置

坐标轴定义了数据的展示尺度,错误配置会导致图表误导。主要验证X轴和Y轴。

  1. X轴(xAxis):验证其类型('category'分类轴如日期,或'value'数值轴)、数据(data,对于分类轴)是否与预期一致。
  2. Y轴(yAxis):验证其类型(通常是'value')、最小值(min)、最大值(max,有时自动计算)、轴标签格式(axisLabel.formatter)等。例如,需要验证Y轴的单位(如“万元”)是否正确显示。

验证示例代码:

def test_chart_axes(self): option = get_echarts_option(self.driver, chart_container) # 验证X轴 x_axis = option['xAxis'][0] # 通常是一个列表 assert x_axis['type'] == 'category', "X轴类型应为分类轴" expected_categories = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'] assert x_axis['data'] == expected_categories, f"X轴分类数据错误" # 验证Y轴 y_axis = option['yAxis'][0] assert y_axis['type'] == 'value', "Y轴类型应为数值轴" # 验证Y轴标签格式是否包含单位(例如:格式化函数或字符串包含‘万’) if 'axisLabel' in y_axis and 'formatter' in y_axis['axisLabel']: formatter = y_axis['axisLabel']['formatter'] # 如果formatter是字符串,检查是否包含单位 if isinstance(formatter, str) and '万' in formatter: print("Y轴单位验证通过(包含‘万’)") # 注意:如果formatter是函数,在JS执行环境中才能判断,这里可能无法直接验证内容。

注意事项:坐标轴的formatter有时是一个JavaScript函数字符串,在Python层面无法直接解析其逻辑。对于复杂的格式验证,可能需要通过更间接的方式,比如验证渲染后的DOM文本内容。

4.3 验证提示框(Tooltip)与交互

Tooltip是用户与图表交互的关键反馈。验证分为两步:一是验证Tooltip的全局格式配置,二是模拟鼠标悬停,验证显示的具体内容。

第一步:验证Tooltip配置

def test_tooltip_config(self): option = get_echarts_option(self.driver, chart_container) if 'tooltip' in option: tooltip_config = option['tooltip'] # 验证触发方式 assert tooltip_config.get('trigger') == 'axis', "Tooltip触发方式应为‘axis’(坐标轴触发)" # 验证格式化器(如果可检查) if 'formatter' in tooltip_config: # 这里可以检查formatter字符串是否包含预期的模板变量,如‘{a}’, ‘{b}’, ‘{c}’ pass

第二步:模拟悬停并验证内容(难点与核心)

这是最具挑战性的一部分。我们需要用Selenium模拟鼠标移动到图表上的特定数据点,然后捕获弹出的Tooltip DOM元素,读取其文本进行验证。

然而,由于折线图通常渲染在Canvas或SVG上,我们无法直接定位到一个“数据点”元素。因此,策略是:通过图表实例的API,获取数据点在屏幕上的像素坐标,然后让鼠标移动到这个坐标。

from selenium.webdriver.common.action_chains import ActionChains def test_tooltip_content_on_hover(self): # 1. 获取图表实例和option option = get_echarts_option(self.driver, chart_container) # 2. 获取第一个序列的数据 series_data = option['series'][0]['data'] # 假设我们想验证第二个数据点(索引1)的Tooltip data_index = 1 # 3. 通过JS,获取该数据点在Canvas内的像素坐标 # ECharts提供了 convertToPixel 方法 js_get_pixel = """ var chartDom = arguments[0]; var chartInstance = echarts.getInstanceByDom(chartDom); var pointInPixel = chartInstance.convertToPixel('grid', [arguments[1], arguments[2]]); return pointInPixel; """ # 参数: dataIndex 是数据索引, value 是该索引对应的数据值 data_value = series_data[data_index] pixel_coords = driver.execute_script(js_get_pixel, chart_container, data_index, data_value) # pixel_coords 返回如 [150, 280], 分别是横纵坐标 # 4. 计算该坐标相对于整个浏览器视口的位置 # 首先获取图表容器的位置 container_location = chart_container.location container_size = chart_container.size # 注意:convertToPixel返回的是相对于图表坐标系原点的坐标。 # 我们需要将其转换为相对于页面视口的坐标。 # 一个更稳健的方法是:让图表实例直接返回相对于页面的坐标,或者通过getBoundingClientRect计算。 # 这里是一个简化示例,假设图表容器没有边框和padding,且坐标原点在容器左上角。 viewport_x = container_location['x'] + pixel_coords[0] viewport_y = container_location['y'] + container_size['height'] - pixel_coords[1] # 注意Y轴方向,页面坐标系与Canvas坐标系可能相反 # 5. 使用ActionChains移动鼠标到该坐标 actions = ActionChains(driver) # 注意:move_to_element_with_offset 需要传入一个元素和偏移量 # 我们以图表容器为基准,进行偏移 actions.move_to_element_with_offset(chart_container, pixel_coords[0], pixel_coords[1]).perform() # 6. 等待并定位Tooltip元素(需要根据实际页面结构来) # Tooltip通常是一个绝对定位的div,有特定的class time.sleep(0.5) # 简单等待Tooltip渲染,生产环境应用显式等待 tooltip_element = driver.find_element(By.CLASS_NAME, 'echarts-tooltip') # 类名需根据实际情况调整 # 7. 验证Tooltip文本内容 tooltip_text = tooltip_element.text expected_partial_text = f"周二 {data_value}" # 假设Tooltip会显示周几和数值 assert expected_partial_text in tooltip_text, f"Tooltip内容不符。期望包含‘{expected_partial_text}’,实际是‘{tooltip_text}’"

踩坑实录:坐标转换是这一步最大的坑。convertToPixel返回的坐标是相对于图表“网格”(grid)的,而图表的容器可能有内边距(padding),且页面可能有滚动。上述计算是一个简化模型。更可靠的做法是,在JS代码中直接使用getBoundingClientRect计算出数据点相对于视口的精确坐标,然后返回给Selenium。这需要更复杂的JS片段。务必在你的具体页面上进行仔细调试。

5. 构建健壮且可维护的测试套件

将上述验证点组织成完整的测试用例,并考虑可维护性。

5.1 测试用例设计与封装

一个好的测试框架应该将页面操作、数据提取和断言分离。我们可以采用Page Object模式(PO)的思想,创建一个ChartPage类来封装所有与图表交互的方法。

class EChartsPage: def __init__(self, driver): self.driver = driver self.chart_locator = (By.ID, 'chart-container') self.wait = WebDriverWait(driver, 10) def get_chart_element(self): return self.wait.until(EC.presence_of_element_located(self.chart_locator)) def get_chart_option(self): chart_el = self.get_chart_element() return self._execute_js_on_chart(chart_el, self._js_get_option) def get_data_point_pixel(self, series_index, data_index): chart_el = self.get_chart_element() # 获取数据值 option = self.get_chart_option() value = option['series'][series_index]['data'][data_index] # 执行JS获取像素坐标 js_code = """ var chart = echarts.getInstanceByDom(arguments[0]); var pixel = chart.convertToPixel('grid', [arguments[1], arguments[2]]); var rect = arguments[0].getBoundingClientRect(); // 返回相对于视口的坐标 return [rect.left + pixel[0], rect.top + pixel[1]]; """ return self.driver.execute_script(js_code, chart_el, data_index, value) def hover_data_point(self, series_index, data_index): coords = self.get_data_point_pixel(series_index, data_index) # 使用ActionChains移动鼠标到绝对坐标(需要先将鼠标移到某个元素上?) # 一种替代方案:使用 `driver.execute_script` 直接触发图表的事件 # 这里演示一个更直接但可能不兼容所有环境的方法:模拟鼠标事件 js_hover = """ var event = new MouseEvent('mousemove', { 'view': window, 'bubbles': true, 'cancelable': true, 'clientX': arguments[0], 'clientY': arguments[1] }); arguments[2].dispatchEvent(event); """ chart_el = self.get_chart_element() self.driver.execute_script(js_hover, coords[0], coords[1], chart_el) time.sleep(0.3) # 等待tooltip显示 def get_tooltip_text(self): # 定位tooltip元素,这里需要根据实际UI调整选择器 try: tooltip = self.wait.until(EC.visibility_of_element_located((By.CLASS_NAME, 'echarts-tooltip'))) return tooltip.text except: return None # 内部JS函数定义 _js_get_option = """ var chart = echarts.getInstanceByDom(arguments[0]) || arguments[0].__echarts_instance__; return chart ? chart.getOption() : null; """ def _execute_js_on_chart(self, element, js_template, *args): full_js = f"var chartDom = arguments[0]; {js_template}" return self.driver.execute_script(full_js, element, *args)

然后,测试用例会变得非常清晰:

import pytest class TestLineChart: @pytest.fixture(autouse=True) def setup(self): self.driver = webdriver.Chrome() self.page = EChartsPage(self.driver) self.driver.get(TEST_URL) yield self.driver.quit() def test_chart_data_integrity(self): option = self.page.get_chart_option() # 具体的断言逻辑 assert option['series'][0]['data'] == EXPECTED_DATA assert option['xAxis'][0]['data'] == EXPECTED_CATEGORIES def test_tooltip_for_specific_point(self): # 悬停在第二个数据点 self.page.hover_data_point(series_index=0, data_index=1) tooltip_text = self.page.get_tooltip_text() assert tooltip_text is not None assert "周二" in tooltip_text and "132" in tooltip_text

5.2 处理动态数据与异步加载

很多折线图的数据是异步加载的(通过AJAX)。你的测试脚本必须在数据加载完成、图表渲染完毕后再开始验证。

  1. 使用显式等待:不要用time.sleep。等待图表容器出现后,还可以等待一个特定的标志,比如某个表示数据加载完成的CSS类,或者等待图表实例被创建。
    # 等待图表div存在 wait.until(EC.presence_of_element_located((By.ID, 'chart-container'))) # 自定义等待条件:等待图表option被成功获取(即图表已初始化并有数据) def chart_has_data(driver): option = driver.execute_script("return (window.myChart && window.myChart.getOption()) || null;") return option is not None and option.series and option.series[0].data.length > 0 wait.until(chart_has_data)
  2. 数据驱动测试:如果你的测试数据是变化的,可以将预期数据存储在外部文件(如JSON、YAML)或数据库中。测试用例读取这些数据来进行断言,使得测试数据与代码分离,易于维护。

5.3 常见问题排查与调试技巧

  1. execute_script返回None或报错

    • 可能原因:JS代码有语法错误,或图表实例还未初始化。
    • 排查:首先在浏览器开发者工具的Console中直接运行你的JS代码,确保它能正确返回结果。在Selenium脚本中,可以在执行前后打印日志,或者用try...except捕获异常。
  2. 坐标计算不准,鼠标悬停位置不对

    • 可能原因:容器边框、内边距、图表内边距(grid)未考虑;页面缩放;坐标系转换错误。
    • 排查:在JS代码中,使用console.log输出每一步计算的坐标(convertToPixel的结果、getBoundingClientRect的结果)。在Selenium执行后,通过driver.save_screenshot('debug.png')截图,看看鼠标是否真的移到了正确位置。考虑使用更稳健的dispatchEvent方式直接触发图表内部的鼠标事件,而不是模拟物理鼠标移动。
  3. Tooltip元素定位不到

    • 可能原因:Tooltip的类名或生成方式动态变化;Tooltip还未渲染出来。
    • 排查:使用WebDriverWait并尝试多种定位策略(CSS选择器、XPath)。检查Tooltip是否被渲染到了<body>末尾或其他动态位置。可以尝试通过driver.find_elements查找所有包含特定文本的div。
  4. 测试在CI/CD环境中不稳定

    • 可能原因:无头(Headless)模式下的渲染差异、资源加载速度、机器性能差异。
    • 优化
      • 增加合理的等待时间,优先使用显式等待。
      • 在无头模式下,确保浏览器窗口大小固定,因为图表布局可能响应式变化。
      • 考虑使用driver.set_window_size(1920, 1080)设置固定分辨率。
      • 对于极不稳定的情况,可以引入重试机制(如pytest-retry插件)。

6. 扩展与高级应用场景

掌握了基础验证后,我们可以探索更复杂的场景。

6.1 验证复杂图表类型(如面积图、多折线图)

对于面积图,验证思路与折线图完全一致,只是series.type变为'line'areaStyle不为空。验证多折线图,就是遍历option['series']数组,对每一条线进行上述的数据、名称等验证。关键在于理清series数组的结构与你业务逻辑的对应关系。

6.2 验证视觉样式与响应式布局

虽然不推荐像素级对比,但可以验证一些关键的样式属性是否被正确应用。例如,可以通过JS获取某个图形元素的颜色(itemStyle.color)、线宽(lineStyle.width)等配置信息进行断言。对于响应式,可以改变浏览器窗口大小后,重新获取option,验证某些配置(如gridleft,right百分比值)是否按预期变化。

6.3 与测试报告和CI/CD集成

将你的Selenium图表测试集成到像pytest-html、Allure这样的测试报告框架中。在测试失败时,除了日志,自动截取图表区域的截图甚至整个页面的截图,能极大方便排查问题。可以将这些测试作为CI/CD流水线中的一个阶段,在每次代码提交或部署后自动运行,确保数据可视化功能的质量。

import allure import pytest class TestChartWithReporting: @pytest.fixture(autouse=True) def setup_teardown(self, driver): self.driver = driver self.page = EChartsPage(driver) yield # 每个测试后,如果失败则截图并附加到Allure报告 if hasattr(self, '_test_outcome') and self._test_outcome == 'failed': allure.attach(self.driver.get_screenshot_as_png(), name="failure_screenshot", attachment_type=allure.attachment_type.PNG) def test_chart_with_allure(self): with allure.step("打开图表页面并获取数据"): option = self.page.get_chart_option() allure.attach(str(option), name="chart_option", attachment_type=allure.attachment_type.JSON) with allure.step("验证核心数据"): assert len(option['series']) == 1 # ... 更多步骤和断言

最后,我想分享一个个人体会:自动化测试图表,最难的不是技术,而是对不确定性的管理。动态数据、浏览器差异、异步渲染,这些都会带来波动。我们的策略应该是“抓大放小”,聚焦于核心业务逻辑(数据对不对、交互有没有)的验证,而不是像素完美的视觉还原。通过直接与图表库API交互,我们找到了一个相对稳定可靠的验证层。在实际项目中,这套方法帮我节省了海量的手动回归时间,尤其是在每次数据接口或前端组件更新后,能快速给出质量反馈。希望这份详细的拆解,能帮你把Selenium的“点点点”,升级为更智能的“看看图”。

返回列表