Playwright+DeepSeek做AI自动化测试:一个能跑的入门方案

用Playwright截图发给DeepSeek大模型,让它分析页面结构并生成测试步骤,Playwright再自动执行——试了一下发现可行,写出来给有同样需求的人参考。

起因

项目要做回归测试,打开Selenium脚本一看,30多个用例,一半的元素定位器失效了。前端改了class名,脚本就得跟着改,改了一下午。当时想,大模型能"看"图片,能不能让它帮我分析页面、生成测试步骤?折腾了两天,用Playwright+DeepSeek搭了个能跑的原型。

为什么选Playwright

Selenium的选择器策略比较单一,主要靠CSS Selector和XPath,前端一改就挂。Playwright支持by text、by role等语义化定位,API也更现代(微软维护)。但定位器脆弱这个问题,Playwright也没完全解决。

所以我试了另一个方向:把页面截图丢给大模型,让它告诉我"点哪里、输入什么"。

实现思路

流程分三步:

  • Playwright打开页面,截图
  • 截图发给DeepSeek,返回JSON格式的测试步骤
  • Playwright按步骤执行

好处是,前端改了UI之后,大模型会根据新截图重新理解页面,不用手动改定位器。

环境准备

pip install playwright openai python-dotenv
playwright install chromium

去 platform.deepseek.com 注册拿个API Key,新用户有免费额度。建一个 .env 文件:

DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxx

完整代码

import os
import json
import base64
from playwright.sync_api import sync_playwright
from openai import OpenAI
from dotenv import load_dotenv

load_dotenv()

client = OpenAI(
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    base_url="https://api.deepseek.com"
)

def analyze_page(image_path, task_desc):
    """截图发给大模型,返回测试步骤JSON"""
    with open(image_path, "rb") as f:
        img_b64 = base64.b64encode(f.read()).decode()
    
    resp = client.chat.completions.create(
        model="deepseek-chat",
        messages=[
            {"role": "system", "content": "你是一个测试工程师。根据页面截图和任务描述,生成JSON格式的测试步骤。"},
            {"role": "user", "content": [
                {"type": "text", "text": f"任务:{task_desc}\n\n请分析这个页面,生成测试步骤。"
                    "返回JSON数组,每个步骤包含action(type/click/press)、"
                    "target(页面上的文字描述)、value(需要输入的内容或按键名)。"
                    "只返回JSON,不要其他内容。"},
                {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{img_b64}"}}
            ]}
        ],
        temperature=0.3,
        max_tokens=2000
    )
    
    text = resp.choices[0].message.content.strip()
    # DeepSeek有时会套markdown代码块
    if text.startswith("```"):
        text = text.split("\n", 1)[1]
        text = text.rsplit("```", 1)[0]
    return json.loads(text)

def execute_steps(page, steps):
    """按大模型生成的步骤执行操作"""
    results = []
    for i, step in enumerate(steps):
        action = step.get("action", "click")
        target = step.get("target", "")
        value = step.get("value", "")
        
        try:
            if action == "type":
                locator = page.get_by_placeholder(target)
                if locator.count() == 0:
                    locator = page.get_by_label(target)
                if locator.count() == 0:
                    locator = page.get_by_role("textbox", name=target)
                locator.first.fill(value)
                results.append(f"  [PASS] 步骤{i+1}: 在'{target}'输入'{value}'")
                
            elif action == "click":
                locator = page.get_by_role("button", name=target)
                if locator.count() == 0:
                    locator = page.get_by_text(target)
                if locator.count() == 0:
                    locator = page.locator(f"a:has-text('{target}')")
                locator.first.click()
                results.append(f"  [PASS] 步骤{i+1}: 点击'{target}'")
                
            elif action == "press":
                # 按键前先聚焦到文本输入框,避免键盘事件发到错误元素
                page.locator(
                    "input[type='text'], input[type='search'], textarea"
                ).first.focus()
                page.keyboard.press(value)
                results.append(f"  [PASS] 步骤{i+1}: 按键'{value}'")
                
            else:
                results.append(f"  [SKIP] 步骤{i+1}: 未知动作'{action}'")
                
        except Exception as e:
            results.append(f"  [FAIL] 步骤{i+1}: {action} '{target}' - {e}")
    
    return results

def run_test(url, task_desc, screenshot_dir="screenshots"):
    os.makedirs(screenshot_dir, exist_ok=True)
    
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        page = browser.new_page(viewport={"width": 1280, "height": 720})
        
        print(f"打开页面: {url}")
        page.goto(url, wait_until="networkidle")
        
        ss_path = os.path.join(screenshot_dir, "page.png")
        page.screenshot(path=ss_path)
        print(f"截图已保存: {ss_path}")
        
        print("正在分析页面...")
        steps = analyze_page(ss_path, task_desc)
        print(f"生成 {len(steps)} 个测试步骤:")
        for i, s in enumerate(steps):
            print(f"  {i+1}. {s}")
        
        print("\n执行测试:")
        results = execute_steps(page, steps)
        for r in results:
            print(r)
        
        result_path = os.path.join(screenshot_dir, "result.png")
        page.screenshot(path=result_path)
        
        browser.close()
        
        passed = sum(1 for r in results if "[PASS]" in r)
        failed = sum(1 for r in results if "[FAIL]" in r)
        print(f"\n结果: {passed} passed, {failed} failed")
        return results

if __name__ == "__main__":
    run_test(
        url="https://www.baidu.com",
        task_desc="在百度搜索框输入'Playwright自动化测试',点击搜索按钮,验证搜索结果页包含搜索关键词"
    )

运行

python ai_test.py

输出大概长这样:

打开页面: https://www.baidu.com
截图已保存: screenshots/page.png
正在分析页面...
生成 3 个测试步骤:
  1. {'action': 'type', 'target': '请输入', 'value': 'Playwright自动化测试'}
  2. {'action': 'click', 'target': '百度一下', 'value': ''}
  3. {'action': 'press', 'value': 'Enter'}

执行测试:
  [PASS] 步骤1: 在'请输入'输入'Playwright自动化测试'
  [PASS] 步骤2: 点击'百度一下'
  [PASS] 步骤3: 按键'Enter'

结果: 3 passed, 0 failed

screenshots目录下会生成page.png(执行前)和result.png(执行后)。

踩坑

大模型返回的JSON套了markdown代码块。 DeepSeek有时候会把JSON包在json...里,直接json.loads()会炸。代码里做了处理,先strip掉代码块标记再解析。如果模型返回格式更离谱(比如前面加了一段解释文字),建议加重试。

元素定位要多层fallback。 大模型说的target不一定精确——它说"搜索按钮",页面上写的是"百度一下"。所以execute_steps里做了三级fallback:先按role找,再按text找,最后用CSS选择器兜底。实测百度这种简单页面没问题,复杂页面(多个同名按钮、iframe嵌套)可能需要额外处理。

headless模式下动态内容可能还没加载完。 page.goto加了wait_until=“networkidle”,但如果页面有懒加载图片或延迟渲染的组件,截图时可能截不到。这种情况加一行就行:

page.wait_for_timeout(2000)

局限性

这个方案是个原型,离生产可用还有距离。几个问题:

  • 每次测试都调API,有成本。DeepSeek便宜但跑大量用例费用会上来
  • 大模型输出有一定随机性,同一个页面不同次可能生成不同步骤
  • 只能处理单页面操作,涉及多页面跳转或复杂弹窗的场景没覆盖

如果想往生产用,可以考虑加步骤缓存(同样的页面不重复分析)、结果校验(截图对比)、失败重试这些。

参考

  • Playwright Python文档:https://playwright.dev/python/
  • DeepSeek API文档:https://platform.deepseek.com/api-docs
  • OpenAI Python SDK:https://github.com/openai/openai-python

标签:AI应用、自动化测试、Playwright、Python、AI实战

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐