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

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 and relay URL parsing is tested in 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 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 and run_server() in 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):


# 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:

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

Then use the run_relay_server helper to establish a test environment:

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:

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 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—serve as both documentation and integration tests. Convert any example into a test by invoking its run function directly:

#[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 In-process relay spawning for tests run_relay_server()
iroh/tests/integration.rs High-level integration scenarios End-to-end connection tests
iroh-relay/src/lib.rs Relay client and server implementation Client::connect(), Server::run()
iroh-dns-server/src/lib.rs DNS-over-HTTPS test server run_server()
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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →