Skip to main content
Glama
hao-cyber
by hao-cyber
test_phone_functionality.py12.7 kB
import os import json import pytest import asyncio from unittest.mock import patch, MagicMock, AsyncMock # 导入被测试的模块 from phone_mcp.core import check_device_connection, run_command from phone_mcp.tools.call import call_number, end_call from phone_mcp.tools.messaging import ( send_text_message, receive_text_messages, get_raw_messages, ) # 辅助函数,将同步结果转换为异步结果 def async_return(result): """将同步结果包装为异步结果的辅助函数""" loop = asyncio.get_event_loop_policy().get_event_loop() f = loop.create_future() f.set_result(result) return f # 固定装置:模拟ADB命令执行 @pytest.fixture def adb_mock(): """模拟ADB命令执行的装置,可控制命令执行结果""" with patch("phone_mcp.core.run_command", new_callable=AsyncMock) as mock_run: yield mock_run @pytest.mark.asyncio class TestDeviceConnection: """测试设备连接检测功能""" async def test_check_device_connection_connected(self, adb_mock): """测试设备已连接的情况""" # 设置模拟的命令执行返回值 adb_mock.return_value = (True, "List of devices attached\nXXXXXXXX\tdevice") # 执行被测试的函数 result = await check_device_connection() # 验证结果 assert "connected and ready" in result adb_mock.assert_called_with("adb devices") async def test_check_device_connection_not_connected(self, adb_mock): """测试没有设备连接的情况""" # 设置模拟的命令执行返回值,模拟自动重试行为 # 由于check_device_connection中有重试逻辑,这里需要多次设置返回值 adb_mock.side_effect = [ (True, "List of devices attached\n"), # 首次调用 (True, "Success"), # kill-server (True, "Success"), # start-server (True, "List of devices attached\n"), # 第二次调用 (True, "List of devices attached\n"), # 第三次调用 (True, "List of devices attached\n"), # 第四次调用 ] # 执行被测试的函数 result = await check_device_connection() # 验证结果 assert "No device found" in result assert "connected and ready" not in result async def test_check_device_connection_failure(self, adb_mock): """测试命令执行失败的情况""" # 设置模拟的命令执行返回值 adb_mock.return_value = (False, "Command failed: adb not found") # 执行被测试的函数 result = await check_device_connection() # 验证结果 assert "Failed to check device connection" in result assert "adb not found" in result @pytest.mark.asyncio class TestPhoneFunctionality: """测试手机的基本功能,如拨打电话、发送短信等""" async def test_call_number_success(self, adb_mock): """测试成功拨打电话的情况""" # 直接修改phone_mcp.tools.call模块中run_command的引用 with patch( "phone_mcp.tools.call.run_command", new_callable=AsyncMock ) as mock_call: # 设置模拟的命令执行返回值 mock_call.return_value = (True, "Success") # 执行被测试的函数 phone_number = "10086" result = await call_number(phone_number) # 验证结果 assert "+86" + phone_number in result # 注意:函数会自动添加+86前缀 assert "Calling" in result # 验证被调用 assert mock_call.called async def test_call_number_failure(self, adb_mock): """测试拨打电话失败的情况""" # 直接修改phone_mcp.tools.call模块中run_command的引用 with patch( "phone_mcp.tools.call.run_command", new_callable=AsyncMock ) as mock_call: # 设置模拟的命令执行返回值 mock_call.return_value = (False, "Failed to execute command") # 执行被测试的函数 result = await call_number("10086") # 验证结果 assert "Failed to initiate call" in result # 验证被调用 assert mock_call.called async def test_end_call_success(self, adb_mock): """测试成功结束通话的情况""" # 直接修改phone_mcp.tools.call模块中run_command的引用 with patch( "phone_mcp.tools.call.run_command", new_callable=AsyncMock ) as mock_call: # 设置模拟的命令执行返回值 mock_call.return_value = (True, "Success") # 执行被测试的函数 result = await end_call() # 验证结果 assert "Call ended successfully" in result # 验证被调用的参数 mock_call.assert_called_with("adb shell input keyevent KEYCODE_ENDCALL") async def test_end_call_failure(self, adb_mock): """测试结束通话失败的情况""" # 直接修改phone_mcp.tools.call模块中run_command的引用 with patch( "phone_mcp.tools.call.run_command", new_callable=AsyncMock ) as mock_call: # 设置模拟的命令执行返回值 mock_call.return_value = (False, "Failed to execute command") # 执行被测试的函数 result = await end_call() # 验证结果 assert "Failed to end call" in result # 验证被调用的参数 mock_call.assert_called_with("adb shell input keyevent KEYCODE_ENDCALL") async def test_send_text_message_success(self, adb_mock): """测试成功发送短信的情况""" # 直接修改phone_mcp.tools.messaging模块中run_command的引用 with patch( "phone_mcp.tools.messaging.run_command", new_callable=AsyncMock ) as mock_msg: # 模拟多个命令调用的成功情况 mock_msg.side_effect = [ (True, "Success"), # 打开短信app (True, "Success"), # 按键事件1 (True, "Success"), # 按键事件2 (True, "Success"), # 退出app ] # 模拟sleep函数,避免实际等待 with patch("asyncio.sleep", new_callable=AsyncMock) as mock_sleep: # 执行被测试的函数 phone_number = "10086" message = "测试短信" result = await send_text_message(phone_number, message) # 验证结果 assert "+86" + phone_number in result # 注意:函数会自动添加+86前缀 assert "sent" in result # 验证被调用至少一次 assert mock_msg.call_count > 0 async def test_send_text_message_failure(self, adb_mock): """测试发送短信失败的情况 - 第二步失败""" # 直接修改phone_mcp.tools.messaging模块中run_command的引用 with patch( "phone_mcp.tools.messaging.run_command", new_callable=AsyncMock ) as mock_msg: # 模拟第一次调用成功,第二次调用失败 mock_msg.side_effect = [ (True, "Success"), # 打开短信app成功 (False, "Failed"), # 按键事件1失败 ] # 模拟sleep函数,避免实际等待 with patch("asyncio.sleep", new_callable=AsyncMock) as mock_sleep: # 执行被测试的函数 result = await send_text_message("10086", "测试短信") # 验证结果 assert "Failed to navigate to send button" in result async def test_receive_text_messages_success(self, adb_mock): """测试成功获取短信的情况""" # 由于receive_text_messages会调用check_device_connection, # 我们需要先模拟core模块中的check_device_connection with patch( "phone_mcp.core.check_device_connection", new_callable=AsyncMock ) as mock_check: # 模拟设备连接正常 mock_check.return_value = "Device is connected and ready." # 修改phone_mcp.tools.messaging模块中run_command的引用 with patch( "phone_mcp.tools.messaging.run_command", new_callable=AsyncMock ) as mock_msg: # 模拟短信查询返回正确的短信内容 mock_msg.return_value = ( True, """Row: 0 address=10086, body=你好,这是测试短信, date=1628151234567 Row: 1 address=13900139000, body=这是另一条测试短信, date=1628151234000""", ) # 执行被测试的函数 result = await receive_text_messages(limit=2) # 验证结果是否包含短信内容 assert isinstance(result, str) # 尝试解析JSON结果 try: messages = json.loads(result) assert isinstance(messages, list) assert len(messages) <= 2 # 不应超过限制 # 验证短信内容 if messages: first_msg = messages[0] assert any(key in first_msg for key in ["address", "from"]) assert any(key in first_msg for key in ["body", "text"]) except json.JSONDecodeError: # 如果不是JSON格式,应该包含短信相关信息 assert "SMS" in result or "短信" in result async def test_receive_text_messages_no_device(self, adb_mock): """测试没有设备连接时获取短信的情况""" # 模拟设备连接检查失败 with patch( "phone_mcp.core.check_device_connection", new_callable=AsyncMock ) as mock_check: # 返回设备未连接消息 mock_check.return_value = "No device found. Please connect a device and ensure USB debugging is enabled." # 执行被测试的函数 result = await receive_text_messages() # 验证结果 assert "No device found" in result async def test_get_raw_messages_success(self, adb_mock): """测试成功获取原始短信的情况""" # 由于get_raw_messages会调用check_device_connection, # 我们需要先模拟设备连接检查,再模拟短信内容查询 with patch( "phone_mcp.core.check_device_connection", new_callable=AsyncMock ) as mock_check: # 模拟设备连接正常 mock_check.return_value = "Device is connected and ready." # 修改phone_mcp.tools.messaging模块中run_command的引用 with patch( "phone_mcp.tools.messaging.run_command", new_callable=AsyncMock ) as mock_msg: # 模拟短信查询返回正确的短信内容 mock_msg.return_value = ( True, """Row: 0 address=10086, body=Hello, date=1628151234567 Row: 1 address=13900139000, body=世界, date=1628151234000""", ) # 执行被测试的函数 result = await get_raw_messages(limit=2) # 验证结果包含关键信息 assert isinstance(result, str) assert any(text in result for text in ["Found", "SMS", "短信", "消息"]) assert any( text in result for text in ["Hello", "世界", "10086", "13900139000"] ) async def test_get_raw_messages_no_messages(self, adb_mock): """测试没有短信时的情况""" # 由于get_raw_messages会调用check_device_connection, # 我们需要先模拟设备连接检查,再模拟短信内容查询 with patch( "phone_mcp.core.check_device_connection", new_callable=AsyncMock ) as mock_check: # 模拟设备连接正常 mock_check.return_value = "Device is connected and ready." # 修改phone_mcp.tools.messaging模块中run_command的引用 with patch( "phone_mcp.tools.messaging.run_command", new_callable=AsyncMock ) as mock_msg: # 模拟短信查询返回空结果 - 无Row:标记表示没有短信 mock_msg.return_value = (True, "No SMS found") # 执行被测试的函数 result = await get_raw_messages() # 验证结果 assert "Unable to retrieve" in result or "No SMS" in result

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/hao-cyber/phone-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server