Testing
Testing is an essential part of developing reliable smart contracts. Psy provides built-in support for writing and running tests using the #[test] attribute.
Writing Tests
Tests in Psy are functions marked with the #[test] attribute. Create a test file (e.g., test_math.psy):
#![allow(unused)] fn main() { fn add_numbers(a: Felt, b: Felt) -> Felt { return a + b; } fn multiply_numbers(a: Felt, b: Felt) -> Felt { return a * b; } #[test] fn test_addition() { let result = add_numbers(2, 3); assert_eq(result, 5, "2 + 3 should equal 5"); } #[test] fn test_multiplication() { let result = multiply_numbers(4, 5); assert_eq(result, 20, "4 * 5 should equal 20"); } #[test] fn test_zero_multiplication() { let result = multiply_numbers(0, 10); assert_eq(result, 0, "0 * 10 should equal 0"); } }
Running Tests
To run tests, use the dargo test command:
dargo test --file test_math.psy
This will execute all functions marked with #[test] and report the results.
Test Assertions
Psy provides several assertion functions for testing:
assert(condition, "message")- Assert that a condition is trueassert_eq(left, right, "message")- Assert that two values are equalassert_ne(left, right, "message")- Assert that two values are not equal
Example:
#![allow(unused)] fn main() { #[test] fn test_assertions() { let x = 10; let y = 5; assert(x > y, "x should be greater than y"); assert_eq(x - y, 5, "difference should be 5"); assert_ne(x, y, "x and y should not be equal"); } }
Current Limitations
Important: dargo test currently has the following limitations:
- Single-file testing only: Each test file must be run individually
- No module-level testing: You cannot test across multiple modules in a single command
- No test discovery: You must specify the exact file path
These limitations are being addressed in future releases.
Best Practices
- Separate test files: Keep tests in dedicated
.psyfiles separate from your main code - Descriptive names: Use clear, descriptive names for test functions
- Clear messages: Provide helpful assertion messages that explain what went wrong
- Test edge cases: Include tests for boundary conditions and error cases
Example test structure:
#![allow(unused)] fn main() { // Functions to test fn fibonacci(n: Felt) -> Felt { if n <= 1 { return n; }; return fibonacci(n - 1) + fibonacci(n - 2); } // Test cases #[test] fn test_fibonacci_base_cases() { assert_eq(fibonacci(0), 0, "fib(0) should be 0"); assert_eq(fibonacci(1), 1, "fib(1) should be 1"); } #[test] fn test_fibonacci_sequence() { assert_eq(fibonacci(2), 1, "fib(2) should be 1"); assert_eq(fibonacci(3), 2, "fib(3) should be 2"); assert_eq(fibonacci(4), 3, "fib(4) should be 3"); assert_eq(fibonacci(5), 5, "fib(5) should be 5"); } }