# How to Test iroh Code: Unit, Integration, and In-Process Relay Testing

> Learn to test iroh code effectively. Explore unit, integration, and in-process relay testing strategies for robust development. Run all tests with cargo test --workspace --all-features.

- Repository: [number zero/iroh](https://github.com/n0-computer/iroh)
- Tags: how-to-guide
- Published: 2026-06-21

---

**Enable the `test-utils` feature and run `cargo test --workspace --all-features` to execute the complete test suite, or use `iroh::test_utils::run_relay_server()` to spawn in-process relay servers for deterministic, network-isolated integration tests.**

The iroh repository is a multi-crate Rust workspace implementing a QUIC-based networking stack with relay servers, DNS resolution, and protocol handlers. Understanding how to test iroh code requires familiarity with its three-layer testing architecture—unit tests, integration tests, and feature-gated test utilities—and the specific commands used in the project's CI pipeline.

## Understanding iroh's Testing Architecture

The n0-computer/iroh codebase organizes tests into three distinct levels based on scope and dependencies.

### Unit Tests

Unit tests verify pure functions, data type invariants, and internal helpers without external network dependencies. These live alongside production code in `src/**/*.rs` files throughout the workspace. For example, address validation logic is tested in [`iroh/src/address_lookup.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/address_lookup.rs) and relay URL parsing is tested in [`iroh-base/src/relay_url.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/relay_url.rs).

### Integration Tests

Integration tests validate end-to-end behavior of endpoints, relay servers, and DNS resolution. These reside in `iroh/tests/*.rs`, with [`iroh/tests/integration.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/tests/integration.rs) containing the primary high-level scenarios. Unlike unit tests, integration tests in iroh often require spawning real infrastructure—such as relay servers—to verify hole-punching and connection logic.

### Test Utilities Feature

The **`test-utils` feature** unlocks in-process infrastructure helpers that allow tests to run without external network access. When enabled, this feature exposes `run_relay_server()` in [`iroh/src/test_utils.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/test_utils.rs) and `run_server()` in [`iroh-dns-server/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-dns-server/src/lib.rs). These functions create temporary TLS certificates and return guards that automatically clean up resources when dropped, ensuring deterministic execution in CI environments.

## Running the Complete Test Suite

Use the following commands to replicate the exact testing environment used by the project's GitHub Actions workflow ([`.github/workflows/tests.yaml`](https://github.com/n0-computer/iroh/blob/main/.github/workflows/tests.yaml)):

```bash

# Run all unit and integration tests with every optional feature enabled

cargo test --workspace --all-features

# Faster, flaky-aware execution using nextest (recommended for CI)

cargo nextest run --workspace --all-features

```

The `--workspace` flag ensures all crates—including `iroh`, `iroh-relay`, `iroh-base`, and `iroh-dns-server`—are tested together. The `--all-features` flag is critical because it activates the `test-utils` feature required by integration tests.

## Writing Integration Tests with In-Process Relays

To write tests that verify relay-assisted connections without network flakiness, enable the `test-utils` feature in your [`Cargo.toml`](https://github.com/n0-computer/iroh/blob/main/Cargo.toml):

```toml
[dev-dependencies]
iroh = { version = "0.95", features = ["test-utils"] }

```

Then use the `run_relay_server` helper to establish a test environment:

```rust
use iroh::test_utils::run_relay_server;
use iroh::endpoint::{Builder, ProtocolHandler};
use std::sync::Arc;

#[tokio::test]
async fn echo_through_relay() -> anyhow::Result<()> {
    // 1. Spawn an in-process relay server with auto-generated TLS certificates
    let _relay = run_relay_server().await?;

    // 2. Build a client endpoint that accepts the echo protocol
    let client = Builder::bind().await?;
    client
        .router()
        .accept(b"iroh-example/echo/0".to_vec(), Arc::new(Echo))
        .spawn()
        .await?;

    // 3. Connect a second endpoint to the first (relay discovery happens internally)
    let server = Builder::bind().await?;
    let conn = server
        .connect(client.local_addr(), b"iroh-example/echo/0")
        .await?;

    // 4. Open a bidirectional stream, send payload, and verify echo
    let (mut send, mut recv) = conn.open_bi().await?;
    send.write_all(b"hello from test").await?;
    send.finish()?;
    let received = recv.read_to_end(64).await?;
    assert_eq!(received, b"hello from test");

    Ok(())
}

// Minimal echo protocol handler
struct Echo;
#[async_trait::async_trait]
impl ProtocolHandler for Echo {
    async fn accept(&self, conn: iroh::connection::Connection) -> anyhow::Result<()> {
        let (mut send, mut recv) = conn.accept_bi().await?;
        tokio::io::copy(&mut recv, &mut send).await?;
        send.finish()?;
        conn.closed().await;
        Ok(())
    }
}

```

The `run_relay_server()` function handles certificate generation and endpoint configuration automatically, allowing the test to run in isolated CI containers without external relay infrastructure.

## Testing DNS Resolution

The DNS server implementation in `iroh-dns-server` provides its own test helper. To test DNS-over-HTTPS resolution according to the Pkarr specification:

```rust
use iroh_dns_server::run_server;

#[tokio::test]
async fn dns_query_works() -> anyhow::Result<()> {
    // Spins up a temporary DNS-over-HTTPS server on a local port
    let (addr, _guard) = run_server().await?;
    
    // Perform HTTPS queries against `addr` using the generated certificates
    // EndpointId resolution logic is tested against this server
    Ok(())
}

```

This pattern is implemented in [`iroh-dns-server/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-dns-server/src/lib.rs) and allows verification of the discovery protocol without relying on public DNS infrastructure.

## Testing Example Programs

Example programs in `iroh/examples/`—such as [`echo.rs`](https://github.com/n0-computer/iroh/blob/main/echo.rs)—serve as both documentation and integration tests. Convert any example into a test by invoking its run function directly:

```rust
#[tokio::test]
async fn example_echo_still_works() -> anyhow::Result<()> {
    // Reuses the logic from iroh/examples/echo.rs
    iroh_examples::echo::run().await
}

```

This ensures examples remain functional as the underlying `Builder` and `ProtocolHandler` APIs evolve.

## Key Testing Files and Functions

| File Path | Purpose | Key Function |
|-----------|---------|--------------|
| [`iroh/src/test_utils.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/test_utils.rs) | In-process relay spawning for tests | `run_relay_server()` |
| [`iroh/tests/integration.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/tests/integration.rs) | High-level integration scenarios | End-to-end connection tests |
| [`iroh-relay/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/lib.rs) | Relay client and server implementation | `Client::connect()`, `Server::run()` |
| [`iroh-dns-server/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-dns-server/src/lib.rs) | DNS-over-HTTPS test server | `run_server()` |
| [`iroh/examples/echo.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/echo.rs) | Reference protocol implementation | `Echo` protocol handler |

These files contain the authoritative implementations of the testing patterns described in this guide.

## Summary

- **Run the full suite** with `cargo test --workspace --all-features` or `cargo nextest run --workspace --all-features` to match CI behavior.
- **Enable `test-utils`** to access `run_relay_server()` and `run_server()` for deterministic, in-process infrastructure testing.
- **Place unit tests** in `src/**/*.rs` alongside implementation code, and **integration tests** in `iroh/tests/*.rs`.
- **Avoid external network dependencies** by using the provided test helpers that manage TLS certificates and temporary ports automatically.

## Frequently Asked Questions

### How do I run tests for only one specific crate in the workspace?

Use the `--package` flag to target a specific crate. For example, `cargo test --package iroh --lib` runs only the unit tests for the main `iroh` crate, while `cargo test --package iroh-relay` tests the relay implementation specifically.

### What is the `test-utils` feature and when should I use it?

The `test-utils` feature exposes helper functions like `run_relay_server()` that spawn real relay and DNS servers using temporary TLS certificates. You should use this feature when writing integration tests that require relay discovery or DNS resolution but must run in isolated CI environments without external network access.

### How do I test code that requires a relay server without external network access?

Import `iroh::test_utils::run_relay_server` and call `.await?` on it at the start of your async test. This function returns a guard that keeps the server alive for the duration of the test and automatically handles cleanup. The relay runs on a local port with self-signed certificates that the test endpoints trust automatically.

### Can I use `nextest` instead of `cargo test` for iroh?

Yes. The iroh CI pipeline uses `cargo nextest run --workspace --all-features` because it provides faster execution and better handling of flaky tests. Install `nextest` via `cargo install cargo-nextest` and use it as a drop-in replacement for `cargo test` when running the full workspace suite.