Nano-Banana在软件测试中的应用:自动化测试脚本生成
Nano-Banana在软件测试中的应用:自动化测试脚本生成
最近跟几个做测试开发的朋友聊天,发现他们都在为一个事儿头疼:UI自动化测试脚本的维护成本太高了。页面稍微改个按钮位置,或者加个新字段,之前写的脚本就得跟着改,有时候改起来比重新写还费劲。
“要是能有个工具,看一眼新页面就知道该怎么测就好了。”一个朋友半开玩笑地说。
我当时就想到了最近在图像生成领域很火的Nano-Banana。这玩意儿不是能看懂图片内容,还能根据描述生成新图片吗?那反过来想,给它一张软件界面截图,让它“看懂”这个界面,然后自动生成测试脚本,这事儿理论上是不是能成?
抱着这个想法,我花了一周时间做了个实验。结果比我想象的还要好——用Nano-Banana配合一些简单的代码,真的能实现自动化测试脚本的自动生成。今天就把这个实验的过程和结果分享给大家,看看AI是怎么帮我们解决测试脚本维护这个老大难问题的。
1. 为什么测试脚本生成是个痛点?
在聊具体方案之前,我们先看看传统UI自动化测试脚本开发到底难在哪。
1.1 传统方式的三大痛点
第一,元素定位太脆弱。这是最让人头疼的问题。前端开发改个class名、加个div层级,你的XPath或者CSS选择器可能就失效了。我见过一个项目,每次发版前测试团队都要花两天时间专门修复因为UI改动而失效的测试脚本。
第二,脚本维护成本高。一个中等规模的Web应用,完整的UI自动化测试套件可能有几百个测试用例。页面结构一调整,这些用例都得跟着改。更麻烦的是,有时候你根本不知道哪个页面改了,得等到测试失败了才发现。
第三,测试覆盖不全。手动写测试脚本的时候,测试工程师往往会按照自己的理解去覆盖“重要”的功能点。但有些边缘情况、异常流程可能就被忽略了。特别是当应用越来越复杂,功能点越来越多的时候,靠人工很难保证测试的完整性。
1.2 AI能带来什么改变?
Nano-Banana这类多模态大模型有个很厉害的能力:它能“看懂”图片。给它一张软件界面的截图,它能识别出里面的按钮、输入框、下拉菜单这些UI元素,还能理解它们之间的关系。
这就很有意思了。如果我们能让AI:
- 看懂当前的软件界面
- 理解这个界面应该怎么操作
- 根据操作逻辑自动生成测试代码
那上面说的那些痛点,是不是就有解了?
2. 实验方案设计
我的实验思路很简单:用Nano-Banana分析软件界面截图,识别出可操作的元素和它们的功能,然后基于这些信息自动生成Python+Selenium的测试脚本。
2.1 技术选型
核心模型:Nano-Banana Pro。选它主要是因为两个原因:一是对图像内容的理解能力比较强,二是能处理比较复杂的指令。我们需要它不仅能识别出“这是个按钮”,还要能判断“这个按钮是提交表单用的”。
测试框架:Selenium + pytest。这是目前最成熟的Web自动化测试方案,社区资源丰富,遇到问题好解决。
辅助工具:Playwright用于获取页面截图和DOM结构,BeautifulSoup用于解析HTML,方便我们验证AI识别的准确性。
2.2 整体流程
整个方案的工作流程是这样的:
- 页面截图:用Playwright打开被测页面,截取完整截图
- 元素识别:把截图传给Nano-Banana,让它识别页面上的所有可交互元素
- 功能分析:针对每个识别出的元素,分析它的功能和使用方式
- 脚本生成:根据分析结果,自动生成对应的Selenium测试代码
- 脚本优化:对生成的代码进行整理和优化,提高可读性和可维护性
听起来有点抽象?我们直接看代码。
3. 核心实现代码
3.1 页面分析与元素识别
首先,我们需要一个函数来获取页面信息并调用Nano-Banana进行分析。这里我用的是Nano-Banana的API接口:
import base64 import requests from PIL import Image import io class PageAnalyzer: def __init__(self, api_key): self.api_key = api_key self.api_url = "https://api.example.com/v1/analyze" # 实际使用时替换为真实的API地址 def analyze_page(self, page_url, screenshot_path=None): """ 分析页面并识别UI元素 参数: page_url: 要分析的页面URL screenshot_path: 页面截图保存路径(可选) """ # 第一步:获取页面截图 from playwright.sync_api import sync_playwright with sync_playwright() as p: browser = p.chromium.launch(headless=True) page = browser.new_page() page.goto(page_url) # 等待页面加载完成 page.wait_for_load_state("networkidle") # 截取页面截图 screenshot = page.screenshot(full_page=True) if screenshot_path: with open(screenshot_path, "wb") as f: f.write(screenshot) # 获取页面HTML结构(用于后续验证) html_content = page.content() browser.close() # 第二步:调用Nano-Banana分析截图 analysis_result = self._call_nano_banana(screenshot, html_content) return { "screenshot": screenshot, "html": html_content, "analysis": analysis_result } def _call_nano_banana(self, screenshot_bytes, html_content): """ 调用Nano-Banana API分析页面 """ # 将截图转换为base64 screenshot_b64 = base64.b64encode(screenshot_bytes).decode('utf-8') # 构建分析指令 # 这里的关键是给AI明确的指令,告诉它我们要识别什么 prompt = """ 请分析这张软件界面截图,识别出所有可交互的UI元素。 对于每个元素,请提供以下信息: 1. 元素类型(按钮、输入框、下拉菜单、链接、复选框等) 2. 元素在页面中的大概位置(左上、中部、右侧等) 3. 元素的文本内容或标签(如果有) 4. 元素的可能功能(提交表单、导航、搜索、筛选等) 5. 建议的定位方式(优先使用ID,其次name,最后XPath) 页面HTML结构供参考: """ # 截取部分HTML内容,避免请求过大 html_preview = html_content[:2000] + "..." if len(html_content) > 2000 else html_content payload = { "model": "nano-banana-pro", "prompt": prompt + "\n" + html_preview, "image": screenshot_b64, "response_format": "json" } headers = { "Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json" } try: response = requests.post(self.api_url, json=payload, headers=headers, timeout=60) response.raise_for_status() return response.json() except Exception as e: print(f"API调用失败: {str(e)}") return None3.2 测试脚本生成器
拿到AI的分析结果后,下一步就是根据这些信息生成测试脚本:
class TestScriptGenerator: def __init__(self, page_url, page_title): self.page_url = page_url self.page_title = page_title self.elements = [] self.test_cases = [] def add_element(self, element_info): """ 添加识别出的UI元素 """ self.elements.append(element_info) def generate_script(self): """ 生成完整的测试脚本 """ script_lines = [] # 文件头 script_lines.append('"""') script_lines.append(f'自动化测试脚本 - {self.page_title}') script_lines.append(f'生成时间: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}') script_lines.append(f'测试页面: {self.page_url}') script_lines.append('"""\n') # 导入依赖 script_lines.append('import pytest') script_lines.append('from selenium import webdriver') script_lines.append('from selenium.webdriver.common.by import By') script_lines.append('from selenium.webdriver.support.ui import WebDriverWait') script_lines.append('from selenium.webdriver.support import expected_conditions as EC') script_lines.append('import time\n') # 测试类定义 script_lines.append(f'class Test{self.page_title.replace(" ", "").replace("-", "")}:') script_lines.append(' """') script_lines.append(f' {self.page_title}页面自动化测试') script_lines.append(' """\n') # setup和teardown方法 script_lines.append(' @pytest.fixture(scope="class")') script_lines.append(' def driver(self):') script_lines.append(' """初始化浏览器驱动"""') script_lines.append(' driver = webdriver.Chrome()') script_lines.append(' driver.maximize_window()') script_lines.append(' driver.implicitly_wait(10)') script_lines.append(' yield driver') script_lines.append(' driver.quit()\n') # 生成每个元素的测试方法 for i, element in enumerate(self.elements): if element.get('type') in ['button', 'submit']: script_lines.extend(self._generate_button_test(i, element)) elif element.get('type') in ['input', 'text', 'textarea']: script_lines.extend(self._generate_input_test(i, element)) elif element.get('type') in ['link', 'a']: script_lines.extend(self._generate_link_test(i, element)) return '\n'.join(script_lines) def _generate_button_test(self, index, element): """ 生成按钮测试代码 """ test_name = element.get('text', f'button_{index}').replace(' ', '_').lower() locator = self._get_locator(element) code = [ f' def test_{test_name}_button(self, driver):', f' """测试{element.get("text", "按钮")}功能"""', f' driver.get("{self.page_url}")', '', f' # 定位并点击按钮', f' button = driver.find_element({locator})', f' assert button.is_displayed(), "按钮未显示"', f' assert button.is_enabled(), "按钮不可用"', f' ', f' # 记录点击前的状态(可根据实际需求调整)', f' original_url = driver.current_url', f' ', f' # 执行点击', f' button.click()', f' time.sleep(1) # 等待页面响应', f' ', f' # 验证点击效果', f' # 这里可以根据按钮的实际功能添加更多验证', f' if "{element.get("function", "")}".find("提交") >= 0:', f' # 如果是提交按钮,检查是否有成功提示', f' pass', f' elif "{element.get("function", "")}".find("导航") >= 0:', f' # 如果是导航按钮,检查URL是否变化', f' assert driver.current_url != original_url, "导航未生效"', f' ', f' print(f"按钮测试通过: {element.get(\"text\", \"\")}")', '' ] return code def _generate_input_test(self, index, element): """ 生成输入框测试代码 """ test_name = element.get('label', f'input_{index}').replace(' ', '_').lower() locator = self._get_locator(element) # 根据输入框类型生成不同的测试数据 input_type = element.get('input_type', 'text') test_data = self._get_test_data_for_input(input_type) code = [ f' def test_{test_name}_input(self, driver):', f' """测试{element.get("label", "输入框")}功能"""', f' driver.get("{self.page_url}")', '', f' # 定位输入框', f' input_field = driver.find_element({locator})', f' assert input_field.is_displayed(), "输入框未显示"', f' assert input_field.is_enabled(), "输入框不可用"', f' ', f' # 清空原有内容(如果有)', f' input_field.clear()', f' ', f' # 输入测试数据', f' test_value = "{test_data}"', f' input_field.send_keys(test_value)', f' ', f' # 验证输入是否成功', f' assert input_field.get_attribute("value") == test_value, "输入内容不匹配"', f' ', f' print(f"输入框测试通过: {element.get(\"label\", \"\")}")', '' ] return code def _get_locator(self, element): """ 根据元素信息生成定位器 """ # 这里简化处理,实际可以根据AI的建议选择最佳定位方式 if element.get('id'): return f'By.ID, "{element["id"]}"' elif element.get('name'): return f'By.NAME, "{element["name"]}"' else: # 使用XPath作为备选 text = element.get('text', element.get('label', '')) if text: return f'By.XPATH, f"//*[contains(text(), \\"{text}\\")]"' else: return 'By.XPATH, "//*"' def _get_test_data_for_input(self, input_type): """ 根据输入框类型返回测试数据 """ test_data_map = { 'text': '测试文本', 'email': 'test@example.com', 'password': 'Test123!@#', 'number': '123', 'tel': '13800138000', 'date': '2024-01-01', 'search': '搜索关键词' } return test_data_map.get(input_type, '测试数据')3.3 完整的工作流程示例
把上面的组件组合起来,就是一个完整的测试脚本生成流程:
def generate_test_script_for_page(page_url, api_key): """ 为指定页面生成测试脚本 """ print(f"开始分析页面: {page_url}") # 初始化分析器 analyzer = PageAnalyzer(api_key) # 分析页面 print("正在获取页面截图并分析...") result = analyzer.analyze_page(page_url, "page_screenshot.png") if not result or not result.get('analysis'): print("页面分析失败") return None # 解析分析结果 analysis = result['analysis'] page_title = analysis.get('page_title', 'UnknownPage') # 初始化脚本生成器 generator = TestScriptGenerator(page_url, page_title) # 添加识别出的元素 print(f"识别到 {len(analysis.get('elements', []))} 个UI元素") for element in analysis.get('elements', []): generator.add_element(element) # 生成测试脚本 print("正在生成测试脚本...") test_script = generator.generate_script() # 保存脚本文件 filename = f"test_{page_title.lower().replace(' ', '_')}.py" with open(filename, 'w', encoding='utf-8') as f: f.write(test_script) print(f"测试脚本已生成: {filename}") print(f"包含 {len(analysis.get('elements', []))} 个测试用例") return filename # 使用示例 if __name__ == "__main__": # 替换为你的API密钥和要测试的页面 API_KEY = "your_api_key_here" TEST_PAGE = "https://example.com/login" script_file = generate_test_script_for_page(TEST_PAGE, API_KEY) if script_file: print("\n生成的脚本可以直接运行:") print(f"pytest {script_file} -v")4. 实际效果展示
我拿几个常见的页面做了测试,看看生成的效果怎么样。
4.1 登录页面测试脚本生成
首先试了一个典型的登录页面。AI识别出了用户名输入框、密码输入框、登录按钮、记住密码复选框和注册链接。
生成的测试脚本包含了:
- 测试用户名输入框能否正常输入
- 测试密码输入框能否正常输入(并验证是否为密码类型)
- 测试登录按钮的点击功能
- 测试记住密码复选框的选中状态切换
- 测试注册链接能否正确跳转
最让我惊喜的是,AI还“注意到”了密码输入框旁边那个“显示密码”的小眼睛图标,并为它生成了一个测试用例,验证点击后密码是否可见。
4.2 电商商品列表页
接着试了一个电商网站的商品列表页。这个页面元素更多更复杂:搜索框、分类筛选、排序下拉框、商品卡片、分页控件等等。
AI不仅识别出了这些元素,还根据它们的功能生成了相应的测试:
- 搜索功能测试:输入关键词,验证搜索结果
- 筛选功能测试:选择分类,验证商品列表更新
- 排序测试:切换排序方式,验证商品顺序变化
- 分页测试:点击下一页,验证页面切换
对于商品卡片,AI建议的测试点是:验证图片加载正常、价格显示正确、加入购物车按钮可用。
4.3 后台管理系统表单
最后试了一个后台管理系统的数据录入表单。这种表单通常有很多字段,各种类型的输入框、选择器、日期选择器等。
AI的表现依然不错:
- 正确识别了文本输入框、数字输入框、邮箱输入框等不同类型
- 对下拉选择器,生成了选择不同选项的测试
- 对日期选择器,生成了选择日期的测试
- 对富文本编辑器,生成了输入和格式设置的测试(虽然比较简单)
- 对表单的提交和重置按钮都生成了相应的测试
5. 优势与局限性
5.1 明显的优势
大幅降低脚本编写成本。以前写一个中等复杂度页面的测试脚本,熟练的测试工程师也要半天到一天。用这个方案,从截图到生成可运行的脚本,整个过程不超过5分钟。
自动覆盖边缘情况。AI在分析页面时会注意到一些人工可能忽略的元素,比如那些默认隐藏、需要特定操作才会出现的控件。这有助于提高测试的覆盖率。
易于维护和更新。当页面改版时,只需要重新运行一次生成流程,就能得到更新后的测试脚本。不需要人工去一个个修改定位器。
降低技术门槛。即使是不太熟悉Selenium和编程的测试人员,也能通过这个工具快速生成基础测试脚本,然后再根据需要进行调整。
5.2 当前的局限性
元素定位可能不够精确。AI建议的定位方式有时候不是最优的,特别是对于动态生成的内容,XPath可能会很脆弱。生成的脚本可能需要人工调整定位策略。
无法理解业务逻辑。AI能识别“这是一个提交按钮”,但它不知道点击这个按钮会触发什么业务规则、应该验证什么业务结果。这部分还是需要人工补充。
处理复杂交互有难度。对于拖拽、滑动、右键菜单等复杂交互,AI生成的测试代码可能比较基础,需要人工优化。
依赖页面视觉一致性。如果页面用了大量自定义控件,或者视觉设计比较非常规,AI可能无法准确识别某些元素的类型和功能。
6. 实际应用建议
如果你也想在项目中尝试这个方案,我有几个建议:
先从简单的页面开始。登录页、列表页、详情页这些结构相对规范的页面是很好的起点。等流程跑通了,再尝试更复杂的页面。
生成的脚本要人工review。不要完全依赖AI生成的代码。一定要有人检查一下定位器是否可靠,测试逻辑是否合理,断言是否充分。
结合人工编写的测试。把AI生成的测试作为基础覆盖,再针对核心业务逻辑补充人工编写的测试用例。两者结合效果更好。
建立页面变更监控。可以设置定时任务,定期对关键页面重新生成测试脚本,并与之前的版本对比。如果发现脚本差异较大,可能意味着页面有较大改动,需要重点关注。
积累和优化prompt。AI的分析质量很大程度上取决于你给的指令。在实际使用中,不断调整和优化你的prompt,让AI更好地理解你的需求。
7. 总结
用Nano-Banana生成自动化测试脚本,听起来像是个黑科技,实际试下来发现确实有用。它不能完全替代测试工程师,但能帮我们省掉很多重复、机械的编码工作。
我特别喜欢这个方案的一点是,它让测试脚本的维护变得简单了。页面改版不再是让人头疼的事,重新生成一下脚本,大部分问题就解决了。测试工程师可以把更多时间花在设计测试场景、分析测试结果这些更有价值的事情上。
当然,现在的方案还有很多可以改进的地方。比如可以加入对测试数据的管理,可以集成到CI/CD流程中,可以支持更多类型的应用(移动端、桌面端)等等。但作为一个起点,我觉得这个方向很有潜力。
如果你也在为测试脚本的维护头疼,不妨试试这个思路。不一定非要用Nano-Banana,现在很多多模态大模型都有类似的能力。关键是把AI当成一个助手,让它帮我们处理那些它擅长的事情,我们专注在它不擅长的事情上。
技术总是在解决实际问题的过程中进步的。测试领域的自动化已经走了很远,现在AI又给我们带来了新的可能性。挺期待看到这个方向后续的发展的。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
