Skip to content

Commit 649e30d

Browse files
committed
update system test example to include using events (#4951)
# Objective - Adds an example of testing systems that handle events. I had a hard time figuring out how to do it a couple days ago so figured an official example could be useful. - Fixes #4936 ## Solution - Adds a `Score` resource and an `EnemyDied` event. An `update_score` system updates the score when a new event comes through. I'm not sure the example is great, as this probably isn't how you'd do it in a real game, but I didn't want to change the existing example too much.
1 parent 39ea1bb commit 649e30d

File tree

1 file changed

+110
-34
lines changed

1 file changed

+110
-34
lines changed

tests/how_to_test_systems.rs

Lines changed: 110 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,30 @@
1-
use bevy::prelude::*;
1+
use bevy::{ecs::event::Events, prelude::*};
22

33
#[derive(Component, Default)]
44
struct Enemy {
55
hit_points: u32,
6+
score_value: u32,
67
}
78

8-
fn despawn_dead_enemies(mut commands: Commands, enemies: Query<(Entity, &Enemy)>) {
9+
struct EnemyDied(u32);
10+
11+
struct Score(u32);
12+
13+
fn update_score(mut dead_enemies: EventReader<EnemyDied>, mut score: ResMut<Score>) {
14+
for value in dead_enemies.iter() {
15+
score.0 += value.0;
16+
}
17+
}
18+
19+
fn despawn_dead_enemies(
20+
mut commands: Commands,
21+
mut dead_enemies: EventWriter<EnemyDied>,
22+
enemies: Query<(Entity, &Enemy)>,
23+
) {
924
for (entity, enemy) in enemies.iter() {
1025
if enemy.hit_points == 0 {
1126
commands.entity(entity).despawn_recursive();
27+
dead_enemies.send(EnemyDied(enemy.score_value));
1228
}
1329
}
1430
}
@@ -21,77 +37,137 @@ fn hurt_enemies(mut enemies: Query<&mut Enemy>) {
2137

2238
fn spawn_enemy(mut commands: Commands, keyboard_input: Res<Input<KeyCode>>) {
2339
if keyboard_input.just_pressed(KeyCode::Space) {
24-
commands.spawn().insert(Enemy { hit_points: 5 });
40+
commands.spawn().insert(Enemy {
41+
hit_points: 5,
42+
score_value: 3,
43+
});
2544
}
2645
}
2746

2847
#[test]
2948
fn did_hurt_enemy() {
30-
// Setup world
31-
let mut world = World::default();
49+
// Setup app
50+
let mut app = App::new();
51+
52+
// Add Score resource
53+
app.insert_resource(Score(0));
3254

33-
// Setup stage with our two systems
34-
let mut update_stage = SystemStage::parallel();
35-
update_stage.add_system(hurt_enemies.before(despawn_dead_enemies));
36-
update_stage.add_system(despawn_dead_enemies);
55+
// Add `EnemyDied` event
56+
app.add_event::<EnemyDied>();
57+
58+
// Add our two systems
59+
app.add_system(hurt_enemies.before(despawn_dead_enemies));
60+
app.add_system(despawn_dead_enemies);
3761

3862
// Setup test entities
39-
let enemy_id = world.spawn().insert(Enemy { hit_points: 5 }).id();
63+
let enemy_id = app
64+
.world
65+
.spawn()
66+
.insert(Enemy {
67+
hit_points: 5,
68+
score_value: 3,
69+
})
70+
.id();
4071

4172
// Run systems
42-
update_stage.run(&mut world);
73+
app.update();
4374

4475
// Check resulting changes
45-
assert!(world.get::<Enemy>(enemy_id).is_some());
46-
assert_eq!(world.get::<Enemy>(enemy_id).unwrap().hit_points, 4);
76+
assert!(app.world.get::<Enemy>(enemy_id).is_some());
77+
assert_eq!(app.world.get::<Enemy>(enemy_id).unwrap().hit_points, 4);
4778
}
4879

4980
#[test]
5081
fn did_despawn_enemy() {
51-
// Setup world
52-
let mut world = World::default();
82+
// Setup app
83+
let mut app = App::new();
84+
85+
// Add Score resource
86+
app.insert_resource(Score(0));
87+
88+
// Add `EnemyDied` event
89+
app.add_event::<EnemyDied>();
5390

54-
// Setup stage with our two systems
55-
let mut update_stage = SystemStage::parallel();
56-
update_stage.add_system(hurt_enemies.before(despawn_dead_enemies));
57-
update_stage.add_system(despawn_dead_enemies);
91+
// Add our two systems
92+
app.add_system(hurt_enemies.before(despawn_dead_enemies));
93+
app.add_system(despawn_dead_enemies);
5894

5995
// Setup test entities
60-
let enemy_id = world.spawn().insert(Enemy { hit_points: 1 }).id();
96+
let enemy_id = app
97+
.world
98+
.spawn()
99+
.insert(Enemy {
100+
hit_points: 1,
101+
score_value: 1,
102+
})
103+
.id();
61104

62105
// Run systems
63-
update_stage.run(&mut world);
106+
app.update();
64107

65-
// Check resulting changes
66-
assert!(world.get::<Enemy>(enemy_id).is_none());
108+
// Check enemy was despawned
109+
assert!(app.world.get::<Enemy>(enemy_id).is_none());
110+
111+
// Get `EnemyDied` event reader
112+
let enemy_died_events = app.world.resource::<Events<EnemyDied>>();
113+
let mut enemy_died_reader = enemy_died_events.get_reader();
114+
let enemy_died = enemy_died_reader.iter(enemy_died_events).next().unwrap();
115+
116+
// Check the event has been sent
117+
assert_eq!(enemy_died.0, 1);
67118
}
68119

69120
#[test]
70121
fn spawn_enemy_using_input_resource() {
71-
// Setup world
72-
let mut world = World::default();
122+
// Setup app
123+
let mut app = App::new();
73124

74-
// Setup stage with a system
75-
let mut update_stage = SystemStage::parallel();
76-
update_stage.add_system(spawn_enemy);
125+
// Add our systems
126+
app.add_system(spawn_enemy);
77127

78128
// Setup test resource
79129
let mut input = Input::<KeyCode>::default();
80130
input.press(KeyCode::Space);
81-
world.insert_resource(input);
131+
app.insert_resource(input);
82132

83133
// Run systems
84-
update_stage.run(&mut world);
134+
app.update();
85135

86136
// Check resulting changes, one entity has been spawned with `Enemy` component
87-
assert_eq!(world.query::<&Enemy>().iter(&world).len(), 1);
137+
assert_eq!(app.world.query::<&Enemy>().iter(&app.world).len(), 1);
88138

89139
// Clear the `just_pressed` status for all `KeyCode`s
90-
world.resource_mut::<Input<KeyCode>>().clear();
140+
app.world.resource_mut::<Input<KeyCode>>().clear();
91141

92142
// Run systems
93-
update_stage.run(&mut world);
143+
app.update();
94144

95145
// Check resulting changes, no new entity has been spawned
96-
assert_eq!(world.query::<&Enemy>().iter(&world).len(), 1);
146+
assert_eq!(app.world.query::<&Enemy>().iter(&app.world).len(), 1);
147+
}
148+
149+
#[test]
150+
fn update_score_on_event() {
151+
// Setup app
152+
let mut app = App::new();
153+
154+
// Add Score resource
155+
app.insert_resource(Score(0));
156+
157+
// Add `EnemyDied` event
158+
app.add_event::<EnemyDied>();
159+
160+
// Add our systems
161+
app.add_system(update_score);
162+
163+
// Send an `EnemyDied` event
164+
app.world
165+
.resource_mut::<Events<EnemyDied>>()
166+
.send(EnemyDied(3));
167+
168+
// Run systems
169+
app.update();
170+
171+
// Check resulting changes
172+
assert_eq!(app.world.resource::<Score>().0, 3);
97173
}

0 commit comments

Comments
 (0)