Suggestion: `App::update_with`
- Dominant language
- Rust
- Stars
- 48.2k
- Forks
- 4.8k
- Avg merge
- 3d 16h
- Merged PRs (30d)
- 171
Description
## What problem does this solve or what need does it fill?
When writing test cases for Bevy systems, it is often desirable to create a new app, modify its world, and then update it to check the system outputs.
So a lot of test cases end up looking like this:
```rust
let mut app = App::new();
/* Build App */
let a = app.world.spawn(...).id();
let b = app.world.spawn(...).id();
/* Spawn more entities, modify components, etc. */
app.update(); // <--- Invoke the systems
/* Check output */
assert!(...);
```
This can be cleaned up a little by borrowing `world` within a scope:
```rust
let mut app = App::new();
/* Build App */
let (a, b) = {
let world = &mut app.world;
let a = world.spawn(...).id();
let b = world.spawn(...).id();
/* Spawn more entities, modify components, etc. */
(a, b)
}
app.update(); // <--- Invoke the systems
/* Check output */
assert!(...);
```
While functionally this is fine, there are 2 issues with it:
1. Modifying the world and the app is not very ergonomic since the `app` owns `world`, but most mutations are on `world`.
2. It's easy to forget to call `app.update()` and wonder why your test is failing
## What solution would you like?
I've tried addressing this issue by creating a new `UpdateWith` trait extension:
```rust
pub trait UpdateWith {
fn update_with(self, f: F) -> R
where
F: Fn(&mut World) -> R;
}
impl UpdateWith for &mut App {
fn update_with(self, f: F) -> R
where
F: Fn(&mut World) -> R,
{
let result = f(&mut self.world);
self.update();
result
}
}
```
In my mind, this simplifies the test cases a bit and makes them more ergonomic:
```rust
let mut app = App::new();
/* Build App */
let (a, b) = app.update_with(|world| {
let a = world.spawn(...).id();
let b = world.spawn(...).id();
/* Spawn more entities, modify components, etc. */
(a, b)
});
/* Check output */
assert!(...);
```
I think Bevy tests would become a lot more convenient for everyone if this was a standard part of Bevy.
## What alternative(s) have you considered?
I'm not 100% sure if `update_with` is the best name. I'm open to suggestions. It made sense to me considering all the other `*_with` functions in Rust.
This could also be implemented as a member function of `App`, without the extension trait. I'm not sure how I feel about that since it doesn't make sense to use `update_with` anywhere outside of tests or examples.
## Additional context
If we're ok with this implementation as is, I can turn this into a PR.
Contributor guide
Research direction
The proposal centers on App::update_with and the World borrow used in Bevy tests. Start by reviewing App, World, and existing *_with APIs, then inspect relevant tests and examples to determine whether the API belongs in production or test support. Done means an agreed API and name, with coverage showing world mutations are followed by an update.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- developer-experience, testing-qa
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100