patrol-write-test

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Order of actions when writing new tests

编写新测试的操作流程

  1. Inspect existing test code for functions that can be reused
  2. Also think if one of existing functions can be adjusted to match its existing usage and new test
  3. Assign test keys to required elements if they are not assigned yet
  4. Start writing test: reuse existing functions + put new test steps in new test file
  5. Write patrol actions directly in the test file, do not create new methods
  6. 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
  7. After full test passes, reorganize new code into reusable functions
  8. Rerun the test after reorganizing to confirm it still passes
  1. 检查现有测试代码,寻找可复用的函数
  2. 同时思考是否可以调整某个现有函数,使其适配现有用法和新测试需求
  3. 若所需元素尚未分配测试键,为其分配测试键
  4. 开始编写测试:复用现有函数 + 将新测试步骤写入新测试文件
  5. 直接在测试文件中编写Patrol操作,不要创建新方法
  6. 在开发过程中频繁运行测试——不要等到完整测试编写完成。完成每个逻辑步骤组后就运行测试,以便尽早发现失败点
  7. 完整测试通过后,将新代码重组为可复用函数
  8. 重组后重新运行测试,确认仍能通过

Patrol MCP Usage

Patrol MCP 使用方法

When working with Patrol tests:
  • Use
    patrol-run({ "testFile": "patrol_test/your_test.dart" })
    to run tests and wait for completion
  • If no session running: starts new session with specified test file
  • If session already running: automatically restarts current tests
  • Use
    patrol-screenshot({ "platform": "android" })
    or
    patrol-screenshot({ "platform": "ios" })
    to capture screenshots for debugging test failures
  • Use
    patrol-quit({})
    to quit the session gracefully
  • Use
    patrol-status({})
    to check current status and recent output
  • Use
    patrol-native-tree({})
    to fetch the current native UI tree hierarchy for writing native interactions and interactions with apps other than the app under test.
使用Patrol测试时:
  • 使用
    patrol-run({ "testFile": "patrol_test/your_test.dart" })
    运行测试并等待完成
  • 若没有会话在运行:启动指定测试文件的新会话
  • 若已有会话在运行:自动重启当前测试
  • 使用
    patrol-screenshot({ "platform": "android" })
    patrol-screenshot({ "platform": "ios" })
    捕获截图,用于调试测试失败问题
  • 使用
    patrol-quit({})
    优雅退出会话
  • 使用
    patrol-status({})
    检查当前状态和最近输出
  • 使用
    patrol-native-tree({})
    获取当前原生UI树层级,用于编写原生交互以及与被测应用之外的应用进行交互。

Patrol Tests Rules

Patrol 测试规则

Patrol API

Patrol API

  • Any file that directly uses Patrol APIs (
    $()
    ,
    .scrollTo()
    ,
    .tap()
    ,
    .enterText()
    ,
    .waitUntilVisible()
    , etc.) should
    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
    $.platform
    methods before implementing test actions
  • Don't use flutter_test package. Use only patrol api
  • Only run tests with Patrol MCP server for single test and
    patrol test
    for all tests. Never use
    flutter test
    command
  • 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的使用模式
    • 查看
      $.platform
      API中是否有对应操作
    • 若代码库中未找到对应方法,请查阅:https://patrol.leancode.co/
    • 确认正确的API方法后再进行实现
  • 在实现测试操作前,务必检查
    $.platform
    方法
  • 不要使用flutter_test包,仅使用Patrol API
  • 仅通过Patrol MCP服务器运行单个测试,使用
    patrol test
    运行所有测试。绝不要使用
    flutter test
    命令
  • 不要自行编写patrolSetUp和patrolTearDown方法

Action Rules

操作规则

  • Don't use
    $.pump
    ,
    waitUntilVisible
    or
    waitUntilExists
    and other wait methods after or before tap, scrollTo and enterText. Patrol handles it automatically. Do this only at the end of the test
  • 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
    waitUntilExists
    及其他等待方法。Patrol会自动处理这些。仅在测试末尾使用这些方法
  • 仅使用键来查找组件
  • 除非绝对必要,否则不要编写try-catch块
  • 编写完测试后,通过MCP服务器运行测试以检查是否可用,若失败则修复
  • 若测试因未找到元素而失败,检查应用代码中是否需要滚动到该元素,并根据需要调整测试

Assertion Rules

断言规则

  • Don't write assertions after actions, but do write them at the end of the test
  • Prefer using
    waitUntilVisible
    as assertion at the end of the test
  • Use
    expect()
    for assertions only when
    waitUntilVisible
    is not enough
  • 不要在操作后编写断言,但要在测试末尾编写断言
  • 优先使用
    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
      $.platform.mobile.grantPermissionWhenInUse
      over
      $.platform.mobile.tap
  • 务必处理流程中出现的原生对话框:
    • 在触发对话框的操作后立即处理对话框
    • 对于原生权限,优先使用
      $.platform.mobile.grantPermissionWhenInUse
      而非
      $.platform.mobile.tap

Test Keys

测试键

  • Assign a key ONLY to widgets involved in testing
  • ONLY add the
    key
    parameter to existing widgets
  • 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或枚举生成
    • 组件数量可变或较多
    • 组件通过循环或列表生成