自然语言移动测试工具

通过自然语言编写移动测试脚本,利用LLM代理(Claude)解释指令并在设备上执行MCP工具调用。

  • by neural-surya
  • Python
  • 本地部署
  • 1 星标
  • 更新于
在 GitHub 查看 neural-surya/mcp-test-nl

使用说明

mcp测试nl

通过MCP进行自然语言移动测试

用简单的英语写移动测试。LLM代理(Claude)解释您的指令,并在设备上执行正确的MCP工具调用。

# MCP test — natural language
async def test_valid_credentials(self, mcp):
    await mcp.execute(
        "Clear the email field, then type 'test@example.com'. "
        "Clear the password field, then type 'password123'. "
        "Hide the keyboard and tap the login button."
    )
    await mcp.verify(
        "The user should be on the Home screen with a welcome message "
        "and a logout button visible."
    )

与用普通Appium编写的相同测试进行比较:

# Non-MCP test — explicit Appium API calls
def test_valid_credentials(self, driver):
    email = driver.find_element(AppiumBy.ID, "com.testlogin:id/email_input")
    email.clear()
    email.send_keys("test@example.com")

    password = driver.find_element(AppiumBy.ID, "com.testlogin:id/password_input")
    password.clear()
    password.send_keys("password123")

    driver.hide_keyboard()
    driver.find_element(AppiumBy.ID, "com.testlogin:id/login_button").click()

    time.sleep(2)

    assert driver.find_element(AppiumBy.ID, "com.testlogin:id/welcome_text").is_displayed()
    assert driver.find_element(AppiumBy.ID, "com.testlogin:id/logout_button").is_displayed()

项目结构

mcp-test-nl/
├── README.md
├── .env.example              # Environment variable template
├── requirements.txt          # Python dependencies
├── pytest.ini                # Pytest configuration
├── test_app/                 # Android test APK + source
│   └── test-login-app.apk
└── tests/
    ├── __init__.py
    ├── conftest.py           # Registers the mcp fixture
    ├── mcp_fixtures.py       # MCPAgent (execute + verify) + transport config
    ├── test_login.py         # MCP natural language tests
    └── non_mcp/              # Plain Appium tests (for comparison)
        ├── __init__.py
        ├── conftest.py       # Appium driver fixture
        └── test_login_appium.py

MCP代理测试的工作原理

Test: "Login with email test@example.com and password password123"
  │
  ▼
MCPAgent.execute(instruction)
  │
  ├─ Discovers available tools from the MCP server (list_tools)
  ├─ Sends instruction + tool definitions to Claude API
  │     │
  │     ▼
  │   Claude decides: call clear_text(), type_text(), tap(), etc.
  │     │
  │     ▼
  ├─ Executes those tool calls on the device via MCP
  ├─ Feeds results back to Claude (agentic loop)
  └─ Repeats until Claude completes all steps
  │
  ▼
MCPAgent.verify(assertion)
  │
  ├─ Claude calls get_page_source() via MCP
  ├─ Inspects the screen state
  └─ Returns PASS or raises AssertionError with FAIL reason

测试代码对底层工具一无所知。它与 任何MCP服务器 --Appium、Slack、GitHub或任何通过MCP公开工具的东西。


设置

先决条件

  • Python 3.10+
  • 运行在本地主机4723上的Appium服务器
  • 安装了测试应用程序的Android模拟器或设备
  • MCP服务器(本地或远程)

安装

cd mcp-test-nl

python -m venv .venv
source .venv/bin/activate

pip install -r requirements.txt

配置

cp .env.example .env
# Edit .env with your values

运行测试

仅MCP测试(不包括非MCP)

ANTHROPIC_API_KEY=sk-ant-... \
MCP_SERVER_DIR=/path/to/appium_mcp_server \
pytest tests/test_login.py -v -s

仅限非MPCP测试(普通Appium,不需要API密钥)

pytest tests/non_mcp/ -v -s

所有测试

ANTHROPIC_API_KEY=sk-ant-... \
MCP_SERVER_DIR=/path/to/appium_mcp_server \
pytest -v -s

MCP传输:stdio与SSE

MCP夹具支持两种传输模式。这决定了 测试如何连接到MCP服务器.

stdio——本地MCP服务器

夹具将MCP服务器作为 本地子流程 并通过stdin/stdout进行通信。服务器代码必须在您的计算机上。

ANTHROPIC_API_KEY=sk-ant-... \
MCP_TRANSPORT=stdio \
MCP_SERVER_DIR=/path/to/appium_mcp_server \
pytest tests/test_login.py -v -s

MCP_TRANSPORT=stdio 是默认值,因此可以省略它:

ANTHROPIC_API_KEY=sk-ant-... \
MCP_SERVER_DIR=/path/to/appium_mcp_server \
pytest tests/test_login.py -v -s

空气中的介质:

变量 默认值 描述
MCP_SERVER_DIR (必填) MCP服务器项目的根目录
MCP_SERVER_SCRIPT server.py 服务器脚本相对于 MCP_SERVER_DIR
MCP_SERVER_PYTHON .venv/bin/python Python二进制相对 MCP_SERVER_DIR

SSE——远程MCP服务器

夹具连接到 已在运行MCP服务器 通过HTTP使用服务器发送事件。本地不需要服务器代码,只需要URL。

ANTHROPIC_API_KEY=sk-ant-... \
MCP_TRANSPORT=sse \
MCP_SERVER_URL=http://your-server.com/sse \
pytest tests/test_login.py -v -s

SSE信封:

变量 默认值 描述
MCP_SERVER_URL (必填) SSE端点的完整URL

不同场景的SSE示例:

# MCP server on a colleague's machine
MCP_SERVER_URL=http://192.168.1.50:8000/sse

# MCP server deployed on cloud
MCP_SERVER_URL=https://mcp.your-company.com/sse

# MCP server running in Docker locally
MCP_SERVER_URL=http://localhost:8000/sse

何时使用哪个

|站起来| |---|---|---| |MCP服务器在您的计算机上|是|-| |MCP服务器远程托管|-|是| |MCP服务器在Docker/云中运行|-|是| |需要本地服务器源代码|是|否| |它如何连接|启动子进程|HTTP到正在运行的服务器|


MCPAgent API

这 mcp 夹具产生 MCPAgent 有两种方法:

await mcp.execute(instruction: str) -> str

在设备上执行自然语言指令。

  • Claude收到指令以及MCP服务器的可用工具
  • 它决定通过代理循环调用哪些工具(以及以何种顺序)
  • 返回所做工作的文本摘要
await mcp.execute("Type 'hello' into the search bar and tap the search button")

await mcp.verify(assertion: str) -> None

验证当前屏幕状态的条件。

  • Claude使用MCP工具检查设备屏幕
  • 加薪 AssertionError 如果条件不满足,则有失败的原因
await mcp.verify("Search results should be displayed with at least one item")

MCP与非MCP:并排比较

测试代码

MCP(自然语言) — tests/test_login.py

async def test_valid_credentials(self, mcp):
    await mcp.execute(
        "Clear the email field, then type 'test@example.com'. "
        "Clear the password field, then type 'password123'. "
        "Hide the keyboard and tap the login button."
    )
    await mcp.verify(
        "The user should be on the Home screen with a welcome message "
        "and a logout button visible."
    )

非MCP(普通Appium) — tests/non_mcp/test_login_appium.py

def test_valid_credentials(self, driver):
    email = driver.find_element(AppiumBy.ID, "com.testlogin:id/email_input")
    email.clear()
    email.send_keys("test@example.com")

    password = driver.find_element(AppiumBy.ID, "com.testlogin:id/password_input")
    password.clear()
    password.send_keys("password123")

    driver.hide_keyboard()
    driver.find_element(AppiumBy.ID, "com.testlogin:id/login_button").click()

    time.sleep(2)

    assert driver.find_element(AppiumBy.ID, "com.testlogin:id/welcome_text").is_displayed()
    assert driver.find_element(AppiumBy.ID, "com.testlogin:id/logout_button").is_displayed()

差异

MCP(自然语言) 非MCP(普通应用程序)
测试代码 简明英文说明 元素ID、Appium API调用
代码行数 2次调用(执行+验证) 每次测试12行以上
元素定位器 测试代码中没有 到处都是硬编码
可读性 任何人都能读写 需要Appium知识
当UI更改时 说明通常仍然有效 必须更新每个损坏的定位器
谁决定步骤 运行时的Claude(LLM) 编写时的开发人员
依赖项 MCP服务器+Anthropic API 仅限Appium
执行速度 较慢(LLM往返) 较快(直接通话)
成本 每次测试运行API使用量 免费
决定论 LLM可能会改变方法 每次都完全相同

何时使用哪个

  • MCP测试 --当您需要快速的测试编写、可读的测试和对UI更改的弹性时。最适合端到端流,其中“什么”比“如何”更重要。
  • 非MCP测试 -当您需要速度、确定性、零API成本或对精确交互的精确控制时。最适合在CI中频繁运行的回归套件。

编写新测试

MCP测试

在中创建文件 tests/例如。 tests/test_signup.py:

import pytest

@pytest.mark.asyncio
class TestSignup:

    async def test_successful_signup(self, mcp):
        await mcp.execute(
            "Tap the 'Sign Up' link on the login screen. "
            "Fill in 'John' for first name, 'Doe' for last name, "
            "'john@example.com' for email, and 'SecurePass1!' for password. "
            "Tap the Sign Up button."
        )
        await mcp.verify("A success message or welcome screen should be displayed.")

非MCP测试

在中创建文件 tests/non_mcp/:

from appium.webdriver.common.appiumby import AppiumBy

class TestSignup:

    def test_successful_signup(self, driver):
        driver.find_element(AppiumBy.ID, "com.testlogin:id/signup_link").click()
        driver.find_element(AppiumBy.ID, "com.testlogin:id/first_name").send_keys("John")
        driver.find_element(AppiumBy.ID, "com.testlogin:id/last_name").send_keys("Doe")
        driver.find_element(AppiumBy.ID, "com.testlogin:id/email").send_keys("john@example.com")
        driver.find_element(AppiumBy.ID, "com.testlogin:id/password").send_keys("SecurePass1!")
        driver.find_element(AppiumBy.ID, "com.testlogin:id/signup_button").click()
        assert driver.find_element(AppiumBy.ID, "com.testlogin:id/success_msg").is_displayed()

配置参考

变量 默认值 描述
ANTHROPIC_API_KEY (MCP测试需要) Claude的Anthropic API密钥
MCP_TRANSPORT stdio 运输类型: stdio 或 sse
MCP_SERVER_DIR (stdio需要) 本地MCP服务器的根目录
MCP_SERVER_SCRIPT server.py 服务器脚本相对于 MCP_SERVER_DIR
MCP_SERVER_PYTHON .venv/bin/python Python二进制相对 MCP_SERVER_DIR
MCP_SERVER_URL (sse必需) 远程服务器的sse端点URL
MCP_AGENT_MODEL claude-sonnet-4-5-20250929 代理使用的Claude模型
MCP_AGENT_MAX_TURNS 15 每次执行/验证调用的最大工具调用轮次

资源