Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 128 additions & 1 deletion client/pages/getting-started/basics/testing/unit_testing.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,130 @@
# Unit tests

Under development...
Unit testing is a crucial aspect of software development, particularly in blockchain game development, where the integrity and reliability of smart contracts are paramount. This documentation provides a comprehensive overview of unit testing in the Dojo Engine, focusing on testing strategies, Cairo testing patterns, and practical examples using the Player model implementation.

## Importance of Unit Testing in Blockchain Game Development

Unit tests help ensure that individual components of the game function correctly. They allow developers to catch bugs early in the development process, facilitate code refactoring, and provide documentation for the expected behavior of the code. In the context of blockchain games, where transactions and state changes are immutable, thorough testing is essential to prevent costly errors.

## Testing Strategies

When testing models, systems, and helper functions in the Dojo Engine, consider the following strategies:

1. **Isolation**: Each unit test should focus on a single component, ensuring that tests do not interfere with one another.
2. **Edge Cases**: Always test edge cases to ensure that the code behaves as expected under unusual conditions.
3. **Assertions**: Use assertions to validate the expected outcomes of tests, ensuring that the actual results match the expected results.
4. **Test Coverage**: Aim for high test coverage to ensure that all parts of the code are tested, reducing the likelihood of undetected bugs.

## Cairo Testing Patterns

Cairo provides a structured approach to testing smart contracts. Key patterns include:

- **Setup and Teardown**: Use setup functions to initialize the state before tests and teardown functions to clean up afterward.
- **Mocking**: When testing components that depend on external systems, use mocking to simulate those dependencies.
- **Parameterized Tests**: Use parameterized tests to run the same test logic with different inputs, improving test coverage with less code.

## Practical Examples Using the Player Model Implementation

The Player model consists of a struct and several traits that facilitate its functionality. Below are examples of unit tests for the Player model.

### Player Struct

```cairo
struct Player {
address: ContractAddress,
current_beast_id: u16,
battles_won: u16,
battles_lost: u16,
last_active_day: u32,
creation_day: u32,
}
```

### PlayerAssert Trait

```cairo
trait PlayerAssert {
func assert_exists(player: Player);
func assert_not_exists(player: Player);
}
```

### ZeroablePlayerTrait Trait

```cairo
trait ZeroablePlayerTrait {
func zero() -> Player;
func is_zero(player: Player) -> bool;
func is_non_zero(player: Player) -> bool;
}
```

### Tests Module

```cairo
mod Tests {
use super::*;

#[test]
fn test_player_initialization() {
let player = Player {
address: /* some address */,
current_beast_id: 1,
battles_won: 0,
battles_lost: 0,
last_active_day: 0,
creation_day: 0,
};
assert_eq!(player.current_beast_id, 1);
assert_eq!(player.battles_won, 0);
}

#[test]
fn test_player_initialization_zero_values() {
let player = ZeroablePlayerTrait::zero();
assert!(ZeroablePlayerTrait::is_zero(player));
}

#[test]
fn test_player_with_zero_beast() {
let player = Player {
address: /* some address */,
current_beast_id: 0,
battles_won: 0,
battles_lost: 0,
last_active_day: 0,
creation_day: 0,
};
assert_eq!(player.current_beast_id, 0);
}

#[test]
fn test_player_address_uniqueness() {
let player1 = Player {
address: /* address 1 */,
current_beast_id: 1,
battles_won: 0,
battles_lost: 0,
last_active_day: 0,
creation_day: 0,
};
let player2 = Player {
address: /* address 2 */,
current_beast_id: 2,
battles_won: 0,
battles_lost: 0,
last_active_day: 0,
creation_day: 0,
};
assert!(player1.address != player2.address);
}
}
```

## Best Practices for Test Organization

- **Group Related Tests**: Organize tests into modules or files based on functionality to improve readability and maintainability.
- **Descriptive Naming**: Use descriptive names for test functions to clearly indicate what is being tested.
- **Continuous Integration**: Integrate unit tests into the development workflow to ensure that tests are run automatically with each code change.

By following these guidelines and utilizing the provided examples, developers can effectively implement unit testing in the Dojo Engine, ensuring robust and reliable blockchain game applications.