Running Tests
Rust’s testing framework is built intocargo:
Run All Tests
- Unit tests (in
#[cfg(test)]modules) - Integration tests (in
tests/directory) - Documentation tests (in doc comments)
Run Specific Tests
Test a specific module:Show Test Output
By default, Rust captures stdout/stderr. To see print statements:Writing Tests
Unit Tests
Unit tests live in the same file as the code being tested, in a#[cfg(test)] module:
src/commands/ping.rs
src/commands/ping.rs for the complete implementation.
Test Patterns
Annie Mei follows a testable architecture pattern:- Core logic - Transport-agnostic, pure functions
- Adapter - Thin wrapper that calls Serenity APIs
src/commands/ping.rs:
handle_ping() without mocking Discord APIs.
Integration Tests
Thetests/discord_plumbing.rs suite exercises Discord’s HTTP protocol without Discord credentials:
/pingsends the expected immediate callback request- invalid
/animeinput defers, then edits the original interaction response
tests/fixtures/interactions/ use synthetic IDs and tokens. The suite does not call Discord, AniList, MyAnimeList, Spotify, the LLM provider, PostgreSQL, or Redis.
The shared BotCommand catalog in src/commands/mod.rs owns command names, definitions, and dispatch. Its unit tests verify unique names and matching definitions when you run the complete test suite. src/lib.rs exposes command modules to integration tests while src/main.rs remains the executable entry point.
Live Discord smoke tests
Use a private test application and Discord server only when you need to verify the real Gateway, global command registration, permissions, or Discord client rendering. Do not use production credentials or infrastructure. After starting the bot, verify/ping, a deferred /anime response, invalid /anime input, and one /settings component interaction. A member must invoke slash commands in the Discord client; bot accounts cannot invoke them on a member’s behalf.
Mocking External APIs
For commands that call external APIs (AniList, MAL, Spotify), mock the responses:#[cfg(test)] modules beside each implementation for current examples.
Code Quality Tools
Formatting with rustfmt
Format all code according to Rust style guidelines:Linting with Clippy
Run the Clippy linter to catch common mistakes:Type Checking
Fast type checking without building:cargo build and catches most errors.
Code Coverage
Generate code coverage reports usingcargo-tarpaulin:
Install tarpaulin
Generate Coverage Report
tarpaulin-report.html.
For CI/CD, output in XML format:
Benchmarking
Benchmark performance-critical code:benches/fuzzy_bench.rs
Pre-Commit Workflow
Before committing, run this checklist:1
Format Code
2
Run Linter
3
Run Tests
4
Check Types
CI/CD Testing
Annie Mei uses GitHub Actions for automated testing. The workflow:- Run
cargo fmt -- --check - Run
cargo clippy -- -D warnings - Run
cargo test --all-features - Build release binary
.github/workflows/ for workflow definitions.
Test-Driven Development
Follow TDD for new features:1
Write a Failing Test
Start with a test that captures the desired behavior:
2
Run the Test
Verify it fails:
3
Implement the Feature
Write minimal code to make the test pass:
4
Run the Test Again
Verify it passes:
5
Refactor
Improve the implementation while keeping tests green:
Debugging Tests
Print Debugging
Useprintln! or dbg! in tests:
Test-Specific Logging
Enable logging in tests:Running Single Tests in Debug Mode
Best Practices
Test Naming
Use descriptive test names:Format:
function_scenario_expectedBehaviorArrange-Act-Assert
Structure tests clearly:
Test Independence
Each test should be independent:
- Don’t rely on test execution order
- Clean up resources after tests
- Avoid shared mutable state
Edge Cases
Test boundary conditions:
- Empty strings
- Null values (None)
- Maximum/minimum values
- Invalid input
Next Steps
Adding Commands
Learn how to write testable command handlers
Architecture
Understand testable design patterns
Rust Testing Book
Official Rust testing documentation
