> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/block/goose/llms.txt
> Use this file to discover all available pages before exploring further.

# Testing

> Testing patterns and practices in Goose

Goose has a comprehensive test suite covering unit tests, integration tests, and MCP extension tests. This guide explains how to write and run tests.

## Running Tests

### All Tests

Run the entire test suite:

```bash theme={null}
cargo test
```

### Specific Crate

Test a single crate:

```bash theme={null}
cargo test -p goose
cargo test -p goose-server
cargo test -p goose-mcp
```

### Specific Test

Run a specific test by name:

```bash theme={null}
cargo test test_provider_stream
cargo test --package goose --test mcp_integration_test
```

### MCP Integration Tests

Run MCP tests with replay recording:

```bash theme={null}
just record-mcp-tests
```

This runs tests and records MCP interactions to `crates/goose/tests/mcp_replays/`.

### UI Tests

Test the desktop UI:

```bash theme={null}
cd ui/desktop
npm test
```

## Test Organization

### Unit Tests

Unit tests live alongside the code they test:

```rust theme={null}
// In crates/goose/src/providers/base.rs
#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_usage_creation() {
        let usage = Usage::new(Some(10), Some(20), Some(30));
        assert_eq!(usage.input_tokens, Some(10));
        assert_eq!(usage.output_tokens, Some(20));
        assert_eq!(usage.total_tokens, Some(30));
    }
}
```

### Integration Tests

Integration tests are in dedicated `tests/` directories:

```
crates/goose/tests/
├── agent.rs
├── mcp_integration_test.rs
├── providers.rs
└── mcp_replays/
```

Integration tests can import from the crate:

```rust theme={null}
use goose::agents::Agent;
use goose::providers::base::Provider;
```

## Writing Tests

### Basic Test Structure

```rust theme={null}
#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_basic_functionality() {
        // Arrange
        let input = "test";
        
        // Act
        let result = process(input);
        
        // Assert
        assert_eq!(result, "expected");
    }
}
```

### Async Tests

Use `#[tokio::test]` for async tests:

```rust theme={null}
#[tokio::test]
async fn test_async_function() {
    let result = async_operation().await;
    assert!(result.is_ok());
}
```

### Test Case Macro

Use `test_case` for parameterized tests:

```rust theme={null}
use test_case::test_case;

#[test_case("hello", "HELLO" ; "lowercase")]
#[test_case("WORLD", "WORLD" ; "already uppercase")]
fn test_uppercase(input: &str, expected: &str) {
    assert_eq!(input.to_uppercase(), expected);
}
```

### Testing Providers

Create a mock provider for testing:

```rust theme={null}
use goose::providers::base::{Provider, ProviderDef};
use async_trait::async_trait;

#[derive(Clone)]
pub struct MockProvider {
    model_config: ModelConfig,
}

#[async_trait]
impl Provider for MockProvider {
    fn get_name(&self) -> &str {
        "mock"
    }
    
    async fn stream(
        &self,
        _model_config: &ModelConfig,
        _session_id: &str,
        _system: &str,
        _messages: &[Message],
        _tools: &[Tool],
    ) -> Result<MessageStream, ProviderError> {
        let message = Message::assistant()
            .with_text("Test response");
        let usage = ProviderUsage::new(
            "mock".to_string(),
            Usage::default()
        );
        Ok(stream_from_single_message(message, usage))
    }
    
    fn get_model_config(&self) -> ModelConfig {
        self.model_config.clone()
    }
}
```

## MCP Integration Testing

MCP tests verify extension behavior (`crates/goose/tests/mcp_integration_test.rs`).

### Test Structure

```rust theme={null}
#[tokio::test]
async fn test_mcp_tool() {
    // Create extension manager
    let manager = ExtensionManager::new(
        vec![extension_config],
        cancellation_token,
    ).await.unwrap();
    
    // List available tools
    let tools = manager.list_tools().await.unwrap();
    assert!(!tools.is_empty());
    
    // Call a tool
    let result = manager.call_tool(
        "tool_name",
        tool_params,
    ).await.unwrap();
    
    assert!(result.is_success());
}
```

### Recording MCP Interactions

Set `GOOSE_RECORD_MCP=1` to record interactions:

```bash theme={null}
GOOSE_RECORD_MCP=1 cargo test --package goose --test mcp_integration_test
```

Recorded interactions are saved to `crates/goose/tests/mcp_replays/` and can be used for regression testing.

## Test Utilities

### goose-test Crate

Provides utilities for testing:

```rust theme={null}
use goose_test::test_helpers::*;

// Create test session
let session = create_test_session();

// Create test message
let message = test_message("Hello");
```

### goose-test-support Crate

Helpers for integration tests:

```rust theme={null}
use goose_test_support::*;

// Setup test environment
let test_env = TestEnvironment::new();
```

## Testing Extensions

### Extension Test Pattern

```rust theme={null}
use goose::agents::extension::ExtensionConfig;
use goose_mcp::MemoryServer;

#[tokio::test]
async fn test_memory_extension() {
    // Create extension config
    let config = ExtensionConfig::builtin("memory");
    
    // Create extension manager
    let manager = ExtensionManager::new(
        vec![config],
        CancellationToken::new(),
    ).await.unwrap();
    
    // Test tool listing
    let tools = manager.list_tools().await.unwrap();
    let has_memory_tool = tools.iter()
        .any(|t| t.name == "store_memory");
    assert!(has_memory_tool);
}
```

## Testing Best Practices

### Prefer Integration Tests

Per project guidelines, prefer tests in `tests/` directories:

```
crates/goose/tests/
  ├── agent.rs
  ├── providers.rs
  └── mcp_integration_test.rs
```

### Update Self-Test Recipe

When adding features, update `goose-self-test.yaml`:

```bash theme={null}
# After updating the recipe
cargo build
goose run --recipe goose-self-test.yaml
```

### Use Anyhow for Tests

Test functions can return `Result<()>`:

```rust theme={null}
use anyhow::Result;

#[test]
fn test_with_result() -> Result<()> {
    let value = risky_operation()?;
    assert_eq!(value, 42);
    Ok(())
}
```

### Avoid Flaky Tests

* Don't rely on timing/sleep
* Use deterministic test data
* Mock external dependencies
* Clean up resources in tests

### Test Coverage

Aim to test:

* Happy path functionality
* Error cases
* Edge cases (empty input, large input, etc.)
* Async behavior and cancellation
* Resource cleanup

## Continuous Integration

Tests run on every PR via GitHub Actions (`.github/workflows/ci.yml`).

Locally, run the same checks:

```bash theme={null}
just check-everything
```

This runs:

* `cargo fmt` - Code formatting
* `cargo clippy` - Linting
* `cargo test` - All tests
* UI linting
* OpenAPI schema validation

## Debugging Tests

### Show Test Output

```bash theme={null}
cargo test -- --nocapture
```

### Run Single Test

```bash theme={null}
cargo test test_name -- --exact
```

### Enable Logging

```bash theme={null}
RUST_LOG=debug cargo test
```

### Filter Tests

```bash theme={null}
# Run tests matching pattern
cargo test provider

# Run tests in specific module
cargo test providers::tests
```

## Performance Testing

Local inference performance tests:

```bash theme={null}
cargo test --package goose --test local_inference_perf --release
```

## Next Steps

* Learn [Extension Development](/development/extension-development)
* Understand [Provider Interface](/development/provider-interface)
* Explore [Tool Creation](/development/tool-creation)
