Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Testing

This guide covers running and writing tests for job-orchestrator.

Running Tests

All Tests

cargo test

With Output

See println! output from tests:

cargo test -- --nocapture

Specific Test

# By name
cargo test test_upload

# By module
cargo test orchestrator::tests

Ignored Tests

Some tests may be ignored by default (slow, require setup):

cargo test -- --ignored

Test Coverage

Using cargo-tarpaulin

Install:

cargo install cargo-tarpaulin

Generate coverage:

# HTML report
cargo tarpaulin --out Html --output-dir ./coverage

# XML report (for CI)
cargo tarpaulin --out Xml --output-dir ./coverage

View report:

open coverage/tarpaulin-report.html

Test Structure

Tests are organized alongside the code they test:

src/
├── lib.rs
├── orchestrator/
│   ├── mod.rs
│   └── tests.rs      # Orchestrator tests
├── client/
│   ├── mod.rs
│   └── tests.rs      # Client tests
└── utils/
    └── mod.rs        # Inline tests with #[cfg(test)]

Writing Tests

Unit Tests

#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_something() {
        let result = function_under_test();
        assert_eq!(result, expected_value);
    }
}
}

Async Tests

Use tokio::test for async functions:

#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_async_function() {
        let result = async_function().await;
        assert!(result.is_ok());
    }
}
}

Integration Tests

Create files in tests/ directory:

#![allow(unused)]
fn main() {
// tests/integration_test.rs
use job_orchestrator::*;

#[tokio::test]
async fn test_full_workflow() {
    // Setup
    // Test
    // Verify
}
}

Mocking

Using mockall

For trait-based mocking:

#![allow(unused)]
fn main() {
use mockall::automock;

#[automock]
trait Database {
    fn get(&self, id: i32) -> Option<Job>;
}

#[test]
fn test_with_mock() {
    let mut mock = MockDatabase::new();
    mock.expect_get()
        .with(eq(1))
        .returning(|_| Some(Job::default()));

    // Use mock in test
}
}

Using mockito

For HTTP mocking:

#![allow(unused)]
fn main() {
use mockito::Server;

#[tokio::test]
async fn test_http_client() {
    let mut server = Server::new();
    let mock = server.mock("GET", "/health")
        .with_status(200)
        .create();

    // Test against server.url()

    mock.assert();
}
}

Test Utilities

Test Fixtures

Create reusable test data:

#![allow(unused)]
fn main() {
#[cfg(test)]
mod test_utils {
    pub fn create_test_job() -> Job {
        Job {
            id: 1,
            user_id: 1,
            service: "test".to_string(),
            status: Status::Queued,
            ..Default::default()
        }
    }
}
}

Temporary Directories

Use tempfile for temporary test directories:

#![allow(unused)]
fn main() {
use tempfile::TempDir;

#[test]
fn test_file_operations() {
    let temp_dir = TempDir::new().unwrap();
    let file_path = temp_dir.path().join("test.txt");

    // Test file operations

    // TempDir is automatically cleaned up
}
}

CI Testing

Tests run automatically on GitHub Actions:

# .github/workflows/ci.yml
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - run: cargo test

Linting

Clippy

Run Clippy for additional checks:

cargo clippy -- -D warnings

Common fixes:

  • #[allow(clippy::lint_name)] to suppress specific lints
  • Configure in clippy.toml or Cargo.toml

Formatting

Check formatting:

cargo fmt -- --check

Fix formatting:

cargo fmt

Debugging Tests

With println

#![allow(unused)]
fn main() {
#[test]
fn test_debug() {
    let value = compute_something();
    println!("Debug value: {:?}", value);  // Use --nocapture to see
    assert!(value.is_valid());
}
}

With RUST_BACKTRACE

RUST_BACKTRACE=1 cargo test

See Also