patrol-write-test
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseOrder of actions when writing new tests
编写新测试的操作流程
- Inspect existing test code for functions that can be reused
- Also think if one of existing functions can be adjusted to match its existing usage and new test
- Assign test keys to required elements if they are not assigned yet
- Start writing test: reuse existing functions + put new test steps in new test file
- Write patrol actions directly in the test file, do not create new methods
- Run the test frequently during development — don't wait until the full test is written. Run after completing each logical group of steps to catch failures early
- After full test passes, reorganize new code into reusable functions
- Rerun the test after reorganizing to confirm it still passes
- 检查现有测试代码,寻找可复用的函数
- 同时思考是否可以调整某个现有函数,使其适配现有用法和新测试需求
- 若所需元素尚未分配测试键,为其分配测试键
- 开始编写测试:复用现有函数 + 将新测试步骤写入新测试文件
- 直接在测试文件中编写Patrol操作,不要创建新方法
- 在开发过程中频繁运行测试——不要等到完整测试编写完成。完成每个逻辑步骤组后就运行测试,以便尽早发现失败点
- 完整测试通过后,将新代码重组为可复用函数
- 重组后重新运行测试,确认仍能通过
Patrol MCP Usage
Patrol MCP 使用方法
When working with Patrol tests:
- Use to run tests and wait for completion
patrol-run({ "testFile": "patrol_test/your_test.dart" }) - If no session running: starts new session with specified test file
- If session already running: automatically restarts current tests
- Use or
patrol-screenshot({ "platform": "android" })to capture screenshots for debugging test failurespatrol-screenshot({ "platform": "ios" }) - Use to quit the session gracefully
patrol-quit({}) - Use to check current status and recent output
patrol-status({}) - Use to fetch the current native UI tree hierarchy for writing native interactions and interactions with apps other than the app under test.
patrol-native-tree({})
使用Patrol测试时:
- 使用运行测试并等待完成
patrol-run({ "testFile": "patrol_test/your_test.dart" }) - 若没有会话在运行:启动指定测试文件的新会话
- 若已有会话在运行:自动重启当前测试
- 使用或
patrol-screenshot({ "platform": "android" })捕获截图,用于调试测试失败问题patrol-screenshot({ "platform": "ios" }) - 使用优雅退出会话
patrol-quit({}) - 使用检查当前状态和最近输出
patrol-status({}) - 使用获取当前原生UI树层级,用于编写原生交互以及与被测应用之外的应用进行交互。
patrol-native-tree({})
Patrol Tests Rules
Patrol 测试规则
Patrol API
Patrol API
- Any file that directly uses Patrol APIs (,
$(),.scrollTo(),.tap(),.enterText(), etc.) should.waitUntilVisible()import 'package:patrol/patrol.dart'; - ALWAYS inspect Patrol API before implementing test actions:
- Search codebase for existing Patrol API usage patterns
- Check $.platform APIs for the specific action
- If method not found in codebase, check: https://patrol.leancode.co/
- Only implement after confirming the correct API method
- ALWAYS inspect methods before implementing test actions
$.platform - Don't use flutter_test package. Use only patrol api
- Only run tests with Patrol MCP server for single test and for all tests. Never use
patrol testcommandflutter test - Do not write patrolSetUp and patrolTearDown methods on your own
- 任何直接使用Patrol API(、
$()、.scrollTo()、.tap()、.enterText()等)的文件都应.waitUntilVisible()import 'package:patrol/patrol.dart'; - 在实现测试操作前,务必检查Patrol API:
- 在代码库中搜索现有Patrol API的使用模式
- 查看API中是否有对应操作
$.platform - 若代码库中未找到对应方法,请查阅:https://patrol.leancode.co/
- 确认正确的API方法后再进行实现
- 在实现测试操作前,务必检查方法
$.platform - 不要使用flutter_test包,仅使用Patrol API
- 仅通过Patrol MCP服务器运行单个测试,使用运行所有测试。绝不要使用
patrol test命令flutter test - 不要自行编写patrolSetUp和patrolTearDown方法
Action Rules
操作规则
- Don't use ,
$.pumporwaitUntilVisibleand other wait methods after or before tap, scrollTo and enterText. Patrol handles it automatically. Do this only at the end of the testwaitUntilExists - To find widgets, use only keys
- Don't write try catch blocks unless absolutely necessary
- After writing test check if it works by running it with MCP server, fix it if it fails
- If test fails because element was not found, check if it need to be scrolled to in the app code and adjust the test if needed
- 在tap、scrollTo和enterText操作前后,不要使用、
$.pump或waitUntilVisible及其他等待方法。Patrol会自动处理这些。仅在测试末尾使用这些方法waitUntilExists - 仅使用键来查找组件
- 除非绝对必要,否则不要编写try-catch块
- 编写完测试后,通过MCP服务器运行测试以检查是否可用,若失败则修复
- 若测试因未找到元素而失败,检查应用代码中是否需要滚动到该元素,并根据需要调整测试
Assertion Rules
断言规则
- Don't write assertions after actions, but do write them at the end of the test
- Prefer using as assertion at the end of the test
waitUntilVisible - Use for assertions only when
expect()is not enoughwaitUntilVisible
- 不要在操作后编写断言,但要在测试末尾编写断言
- 优先使用作为测试末尾的断言
waitUntilVisible - 仅当不足以满足需求时,才使用
waitUntilVisible进行断言expect()
Native Dialog Handling
原生对话框处理
- ALWAYS handle native dialogs that appear during the flow:
- Handle dialogs immediately after the action that triggers them
- For native permissions prefer over
$.platform.mobile.grantPermissionWhenInUse$.platform.mobile.tap
- 务必处理流程中出现的原生对话框:
- 在触发对话框的操作后立即处理对话框
- 对于原生权限,优先使用而非
$.platform.mobile.grantPermissionWhenInUse$.platform.mobile.tap
Test Keys
测试键
-
Assign a key ONLY to widgets involved in testing
-
ONLY add theparameter to existing widgets
key -
NEVER change widget signatures
-
NEVER refactor existing code structure
-
NEVER hardcode keys in the app, you must use keys from the keys file that is shared between app and tests
-
NEVER create new widgets in the app
-
NEVER create a key that is not assigned to a widget
-
Always make sure that each key value is unique
-
ALWAYS sort keys alphabetically
-
Add keys as first parameter to the widget constructor
-
If widget is not unique (for example generated from a list) use a parameterized key
- ALWAYS prefer using enums or DTOs as the parameter if they already exist
- Use existing widget properties for parameterized keys
- NEVER assign a parameterized key in the app and then use fixed values for it in the keys file (and vice versa)
- NEVER create helper methods, use parameterized keys instead
- When widgets are generated from existing enums or DTOs, always use parameterized keys with the those enum/DTO values as the parameter
Use individual keys when:- Widgets are hardcoded and known at compile time
- Widgets have distinct, meaningful names
Use parameterized keys when:- Widgets are generated from dynamic data
- Widgets are generated from a DTO or enums
- Number of widgets is variable or large
- Widgets are generated in loops or from lists
-
仅为参与测试的组件分配键
-
仅向现有组件添加参数
key -
绝不要更改组件签名
-
绝不要重构现有代码结构
-
绝不要在应用中硬编码键,必须使用应用与测试共享的键文件中的键
-
绝不要在应用中创建新组件
-
绝不要创建未分配给组件的键
-
始终确保每个键值唯一
-
始终按字母顺序对键进行排序
-
将键作为组件构造函数的第一个参数添加
-
若组件不唯一(例如从列表生成),请使用参数化键
- 若已有枚举或DTO,务必优先使用它们作为参数
- 使用现有组件属性作为参数化键的参数
- 绝不要在应用中分配参数化键,然后在键文件中使用固定值(反之亦然)
- 绝不要创建辅助方法,而是使用参数化键
- 当组件从现有枚举或DTO生成时,始终使用以这些枚举/DTO值作为参数的参数化键
在以下情况使用独立键:- 组件是硬编码的,且在编译时已知
- 组件具有独特、有意义的名称
在以下情况使用参数化键:- 组件从动态数据生成
- 组件从DTO或枚举生成
- 组件数量可变或较多
- 组件通过循环或列表生成