mcpField
B
bevy-testing

bevy-testing

github.com/chrisgliddon测试与质量2666 次安装9 周前更新
代码仓库

关于 bevy-testing

bevy-testing 是面向 Bevy 0.19 项目的确定性测试技能,适用于需要验证 ECS 行为、时间、异步任务或渲染结果的开发者。它强调由测试控制时钟与输入,并通过可观察状态或图像捕获检查结果。

主要功能

  1. 01构建最小化 App 测试环境
  2. 02手动控制时间并推进固定调度
  3. 03检查世界状态、消息和观察者效果
  4. 04在无需休眠的情况下测试异步任务
  5. 05捕获渲染结果进行视觉回归

适用场景

  1. 01验证固定调度和计时器行为
  2. 02测试消息、观察者及延迟命令
  3. 03确定性地检查异步任务完成情况
  4. 04将渲染截图与基线进行比较

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/chrisgliddon/bevy-skills --skill bevy-testing

安装后在 Claude Code 中输入/bevy-testing确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
app-and-time.md
messages-observers-and-async.md
visual-regression.md

详细介绍

Bevy 0.19 — 确定性测试

何时使用此技能

  • 系统/插件需要一个最小化的 App 测试框架,并直接断言 world 状态。
  • 固定调度、计时器、消息或观察者需要以确定性方式推进。
  • 异步工作必须在测试中完成,且不能使用任意时长的休眠。
  • 渲染输出需要通过捕获进行视觉回归测试。

构建一个仅包含被测行为的最小化 App,控制其时钟和输入,显式推进,然后检查 world 状态或输出。普通单元测试/集成测试中不要调用 App::run()。

规范模式

use bevy::{prelude::*, time::{TimePlugin, TimeUpdateStrategy}};

#[derive(Resource, Default)]
struct TickCount(u32);

fn count_tick(mut count: ResMut<TickCount>) {
    count.0 += 1;
}

#[test]
fn fixed_system_runs_exactly_three_times() {
    let mut app = App::new();
    app.add_plugins(TimePlugin)
        .insert_resource(Time::<Fixed>::from_hz(60.0))
        .insert_resource(TimeUpdateStrategy::FixedTimesteps(1))
        .init_resource::<TickCount>()
        .add_systems(FixedUpdate, count_tick);

    // The first update initializes Bevy's real-time clock; it has zero delta.
    app.update();

    for _ in 0..3 {
        app.update();
    }

    assert_eq!(app.world().resource::<TickCount>().0, 3);
}

首次初始化时钟的更新之后,FixedTimesteps(n) 会让每次 app.update() 按固定时间步的 n 倍推进,并恰好运行 n 次固定循环。如果测试必须覆盖零 tick 帧、累积时间或追赶行为,请使用 ManualDuration。如果需要精确的绝对时刻,请使用 ManualInstant,并在每次更新前推进其资源。

注意事项

  • 首次调用 app.update() 会将实时时间初始化为零 delta;在统计 FixedTimesteps tick 前先预热,或者明确断言这一零步帧。
  • 直接运行 FixedUpdate 会绕过固定主调度中的时钟/消息语义。
  • 延迟的 Commands 在到达同步点之前不可见。
  • 异步任务必须使用注入的假实现或有界谓词循环来等待完成,绝不能休眠。
  • DefaultPlugins 会添加大多数 world 测试不需要的平台/渲染/音频状态。

通过可观察的契约进行测试

  • 插入资源/组件,并调用 app.update() 来测试调度行为。
  • 对算法和状态机直接调用纯辅助函数。
  • 读取 world 状态、使用测试自有 MessageCursor 读取消息,或检查观察者的效果。
  • 明确标示延迟命令的边界:只有当调度到达同步点时,命令才会应用。
  • 断言不变量和对外有意义的顺序,而不是任意的查询/实体顺序或同一事件下观察者的顺序。

不休眠地处理异步任务

生产系统应使用 bevy::tasks::futures::check_ready 轮询 Task<T>,或以非阻塞方式清空结果通道。测试应尽可能注入确定性的内联/假执行器。否则,持续推进 App,直到满足可观察的完成谓词;同时设置严格的最大步数/截止时间,并在失败时输出队列/任务状态。绝不要“休眠 100 ms,然后祈祷任务已经完成”。

除非已分离,否则丢弃 Bevy Task 会取消它。明确管理任务所有权,并确保测试覆盖完成、取消、失败和过期结果路径。

无头测试与渲染测试

对于 ECS/游戏逻辑,使用 App::new(),并只添加必需的插件(TimePlugin、AssetPlugin 等)。如果契约不涉及窗口、渲染器、音频设备或文件系统,就避免使用 DefaultPlugins。对于渲染验收,启动真实的渲染 App,捕获 Screenshot,等待 ScreenshotCaptured,并将结果与已保存的版本化基线进行比较,使用文档中说明的容差。

选择相关的深入指南

问题阅读
最小化 App、手动时间、固定循环、调度推进App 和时间
消息、观察者、延迟效果、任务和无休眠清空消息、观察者与异步
屏幕截图捕获、确定性场景、差异阈值和产物视觉回归

Review checklist

  • 测试掌控时间;墙上时钟的快慢不会改变预期结果。
  • 每项测试只安装被测对象所需的插件和资源。
  • 显式指定输入和随机种子,并隔离持久状态和全局状态。
  • 固定输入测试涵盖每次更新执行零个、一个和多个固定刻度的情况。
  • 有意在写入前后创建消息游标。
  • 异步测试使用确定性的模拟对象,或使用带有效失败提示的有界进度循环。
  • 视觉测试失败时保留实际图像、预期图像、差异图像以及运行元数据。
  • 仅通过明确且经过审核的操作更新基线。

另请参阅

评论

支持 Markdown