Write tests for Dojo models and systems using spawn_test_world, cheat codes, and assertions. Use when testing game logic, verifying state changes, or ensuring system correctness.
Generates comprehensive Cairo tests for Dojo models and systems using spawn_test_world and cheat codes. Use when you need to test game logic, verify state changes, or ensure system correctness after writing models or systems.
/plugin marketplace add dojoengine/book/plugin install book@dojoengineThis skill is limited to using the following tools:
Write comprehensive tests for your Dojo models and systems using Cairo's test framework and Dojo's cheat codes.
Generates test files with:
spawn_test_world() setupInteractive mode:
"Write tests for the spawn system"
I'll ask about:
Direct mode:
"Test that the move system correctly updates Position"
#[cfg(test)]
mod tests {
use dojo::model::{ModelStorage, ModelValueStorage};
use dojo::world::WorldStorageTrait;
use dojo_cairo_test::{spawn_test_world, NamespaceDef, TestResource};
use super::{Position, IActionsDispatcher, IActionsDispatcherTrait};
#[test]
fn test_spawn() {
// 1. Set up test world
let ndef = NamespaceDef {
namespace: "dojo",
resources: [
TestResource::Model(m_Position::TEST_CLASS_HASH),
TestResource::Contract(actions::TEST_CLASS_HASH),
].span()
};
let mut world = spawn_test_world([ndef].span());
// 2. Deploy system
let actions_address = world.deploy_contract("actions", actions::TEST_CLASS_HASH);
let actions = IActionsDispatcher { contract_address: actions_address };
// 3. Execute action
actions.spawn();
// 4. Verify results
let player = starknet::get_caller_address();
let position: Position = world.read_model(player);
assert(position.x == 0, 'wrong x');
assert(position.y == 0, 'wrong y');
}
}
Test individual functions:
#[test]
fn test_model_creation() {
let position = Position { player: 0x123.try_into().unwrap(), x: 5, y: 10 };
assert(position.x == 5, 'x should be 5');
}
Test systems with models:
#[test]
fn test_spawn_and_move() {
// Setup world
let mut world = spawn_test_world(...);
// Test spawn
actions.spawn();
let pos: Position = world.read_model(player);
assert(pos.x == 0, 'spawn at origin');
// Test move
actions.move(Direction::Right);
let pos: Position = world.read_model(player);
assert(pos.x == 1, 'moved right');
}
Test boundaries and limits:
#[test]
fn test_health_cannot_go_negative() {
// Attack with more damage than health
actions.attack(target, 9999);
let health: Health = world.read_model(target);
assert(health.current == 0, 'health floored at 0');
}
warp: Set block timestamp
use dojo_cairo_test::warp;
warp(world, 1000); // Set timestamp to 1000
actions.process_tick();
roll: Set block number
use dojo_cairo_test::roll;
roll(world, 100); // Set block number to 100
prank: Impersonate caller
use dojo_cairo_test::prank;
let player1 = starknet::contract_address_const::<0x123>();
prank(world, player1); // Next call from player1
actions.spawn();
start_prank / stop_prank: Multiple calls
start_prank(world, player1);
actions.spawn();
actions.move(Direction::Up);
stop_prank(world);
spawn: Deploy contract
let contract_addr = world.deploy_contract("name", class_hash);
#[test]
fn test_position_persistence() {
let mut world = spawn_test_world(...);
// Write
world.write_model(@Position { player: 0x123.try_into().unwrap(), x: 5, y: 10 });
// Read
let pos: Position = world.read_model(0x123.try_into().unwrap());
assert(pos.x == 5, 'x persisted');
assert(pos.y == 10, 'y persisted');
}
#[test]
#[should_panic(expected: ('not authorized',))]
fn test_only_owner_can_call() {
let mut world = spawn_test_world(...);
let unauthorized = starknet::contract_address_const::<0x999>();
prank(world, unauthorized);
actions.admin_function(); // Should panic
}
#[test]
fn test_level_up() {
let mut world = spawn_test_world(...);
// Initial state
let xp: Experience = world.read_model(player);
assert(xp.level == 1, 'starts at level 1');
// Gain XP
actions.gain_xp(1000);
// Verify level up
let xp: Experience = world.read_model(player);
assert(xp.level == 2, 'leveled up');
}
#[test]
fn test_combat_between_players() {
let mut world = spawn_test_world(...);
let player1 = starknet::contract_address_const::<0x111>();
let player2 = starknet::contract_address_const::<0x222>();
// Player 1 spawns
prank(world, player1);
actions.spawn();
// Player 2 spawns
prank(world, player2);
actions.spawn();
// Player 1 attacks Player 2
prank(world, player1);
actions.attack(player2);
// Verify damage
let health: Health = world.read_model(player2);
assert(health.current < 100, 'took damage');
}
test_spawn_places_player_at_origin)#[should_panic]src/
└── tests/
├── test_models.cairo # Model unit tests
├── test_spawn.cairo # Spawn system tests
├── test_movement.cairo # Movement system tests
├── test_combat.cairo # Combat system tests
└── test_integration.cairo # Full workflow tests
# Run all tests
sozo test
# Run specific test
sozo test test_spawn
# Run with output
sozo test -- --nocapture
After writing tests:
dojo-review skill to verify test coveragedojo-deployThis skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.