关于 bevy-testing
bevy-testing 是面向 Bevy 0.19 项目的确定性测试技能,适用于需要验证 ECS 行为、时间、异步任务或渲染结果的开发者。它强调由测试控制时钟与输入,并通过可观察状态或图像捕获检查结果。
主要功能
- 01构建最小化 App 测试环境
- 02手动控制时间并推进固定调度
- 03检查世界状态、消息和观察者效果
- 04在无需休眠的情况下测试异步任务
- 05捕获渲染结果进行视觉回归
适用场景
- 01验证固定调度和计时器行为
- 02测试消息、观察者及延迟命令
- 03确定性地检查异步任务完成情况
- 04将渲染截图与基线进行比较
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ chrisgliddon/ bevy-skills --skill bevy-testing安装后在 Claude Code 中输入/bevy-testing确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/app-and-time.mdmessages-observers-and-async.mdvisual-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;在统计FixedTimestepstick 前先预热,或者明确断言这一零步帧。 - 直接运行
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
- 测试掌控时间;墙上时钟的快慢不会改变预期结果。
- 每项测试只安装被测对象所需的插件和资源。
- 显式指定输入和随机种子,并隔离持久状态和全局状态。
- 固定输入测试涵盖每次更新执行零个、一个和多个固定刻度的情况。
- 有意在写入前后创建消息游标。
- 异步测试使用确定性的模拟对象,或使用带有效失败提示的有界进度循环。
- 视觉测试失败时保留实际图像、预期图像、差异图像以及运行元数据。
- 仅通过明确且经过审核的操作更新基线。
另请参阅
bevy-input-actions— 帧到固定步进的输入不变量。bevy-physics— Rapier 无头运行与确定性边界。bevy-diagnostics-profiling— 验收回放与性能证据。- Bevy
TimeUpdateStrategy - Bevy
Task