Skip to main content
Annie Mei uses Rust’s built-in testing framework along with code quality tools to maintain high standards.

Running Tests

Rust’s testing framework is built into cargo:

Run All Tests

This runs:
  • Unit tests (in #[cfg(test)] modules)
  • Integration tests (in tests/ directory)
  • Documentation tests (in doc comments)

Run Specific Tests

Test a specific module:
Run tests matching a pattern:
Run a single test function:

Show Test Output

By default, Rust captures stdout/stderr. To see print statements:
Show all output including passing tests:

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
See src/commands/ping.rs for the complete implementation.

Test Patterns

Annie Mei follows a testable architecture pattern:
  1. Core logic - Transport-agnostic, pure functions
  2. Adapter - Thin wrapper that calls Serenity APIs
Example from src/commands/ping.rs:
This separation allows testing handle_ping() without mocking Discord APIs.

Integration Tests

The tests/discord_plumbing.rs suite exercises Discord’s HTTP protocol without Discord credentials:
It deserializes synthetic interaction fixtures into Serenity models and directs Serenity’s real HTTP client to a local mock server. The suite verifies:
  • /ping sends the expected immediate callback request
  • invalid /anime input defers, then edits the original interaction response
Fixtures in 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:
See the #[cfg(test)] modules beside each implementation for current examples.

Code Quality Tools

Formatting with rustfmt

Format all code according to Rust style guidelines:
Check formatting without modifying files:
Always run cargo fmt before committing code. This is a required convention.

Linting with Clippy

Run the Clippy linter to catch common mistakes:
Fix warnings automatically (when possible):
Treat warnings as errors:
Fix all Clippy warnings before committing. This ensures code quality.

Type Checking

Fast type checking without building:
This is much faster than cargo build and catches most errors.

Code Coverage

Generate code coverage reports using cargo-tarpaulin:

Install tarpaulin

Generate Coverage Report

This creates an HTML report at tarpaulin-report.html. For CI/CD, output in XML format:

Benchmarking

Benchmark performance-critical code:
benches/fuzzy_bench.rs
Run benchmarks:

Pre-Commit Workflow

Before committing, run this checklist:
1

Format Code

2

Run Linter

Fix all warnings.
3

Run Tests

Ensure all tests pass.
4

Check Types

Verify no type errors.
Consider using a git pre-commit hook to automate this workflow:
.git/hooks/pre-commit
Make it executable:

CI/CD Testing

Annie Mei uses GitHub Actions for automated testing. The workflow:
  1. Run cargo fmt -- --check
  2. Run cargo clippy -- -D warnings
  3. Run cargo test --all-features
  4. Build release binary
See .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

Use println! or dbg! in tests:
Run with output visible:

Test-Specific Logging

Enable logging in tests:

Running Single Tests in Debug Mode

This shows full stack traces on panics.

Best Practices

Test Naming

Use descriptive test names:
Format: function_scenario_expectedBehavior

Arrange-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