High-performance, async-friendly and ergonomic WebAPI testing framework for Rust
Documentation · Getting Started · API Reference · Examples
tanu is a framework for writing end-to-end tests against HTTP, gRPC, and GraphQL APIs in plain Rust. Tests are ordinary async functions; tanu discovers them at compile time, runs them concurrently, and gives you a CLI and an interactive TUI to run and inspect them.
- Plain async Rust tests – annotate an
async fnwith#[tanu::test]; no harness boilerplate. - Parameterized tests – stack
#[tanu::test(args...)]attributes to generate one test case per input. - Concurrent by default – tests run in parallel, with
serialgroups andorderedmodules when you need sequencing. - Built-in HTTP client – every request and response is captured for debugging, with credentials masked automatically.
- gRPC and GraphQL – capture tonic calls through Tower middleware, and send runtime or type-safe GraphQL queries.
- Assertion macros –
check!,check_eq!,check_ne!,check_str_eq!with colored diffs. - Multi-environment projects – run the same suite against
dev,staging, andproductionfrom onetanu.toml. - Retries, filters, fail-fast – configurable exponential backoff, project/module/test filters, and allowlists.
- CLI and TUI – run in CI with the CLI, or browse requests, headers, and payloads interactively in the TUI.
- Pluggable reporters – write your own or use tanu-allure for Allure reports.
Create a binary crate and add tanu with tokio:
cargo new my-api-tests
cd my-api-tests
cargo add tanu
cargo add tokio --features fullReplace src/main.rs with:
use tanu::{check, check_eq, eyre, http::Client};
#[tanu::test]
async fn get_returns_200() -> eyre::Result<()> {
let http = Client::new();
let res = http.get("https://httpbin.org/get").send().await?;
check!(res.status().is_success(), "unexpected status: {}", res.status());
Ok(())
}
// One test case is generated per attribute.
#[tanu::test(200)]
#[tanu::test(404)]
#[tanu::test(500)]
async fn status_codes(expected: u16) -> eyre::Result<()> {
let http = Client::new();
let res = http
.get(format!("https://httpbin.org/status/{expected}"))
.send()
.await?;
check_eq!(expected, res.status().as_u16());
Ok(())
}
#[tanu::main]
#[tokio::main]
async fn main() -> eyre::Result<()> {
let runner = run();
let app = tanu::App::new();
app.run(runner).await?;
Ok(())
}Run the tests:
cargo run -- test # run all tests in CLI mode
cargo run -- test --capture-http # also print HTTP requests/responses
cargo run -- tui # interactive terminal UI
cargo run -- ls # list discovered tests✓ 1 [default] my_api_tests::get_returns_200 (412.08ms)
✓ 2 [default] my_api_tests::status_codes::200 (398.51ms)
✓ 3 [default] my_api_tests::status_codes::404 (401.77ms)
✓ 4 [default] my_api_tests::status_codes::500 (405.12ms)
Tests: 4 passed, 0 failed, 4 total
See the Getting Started guide for a full walkthrough.
Projects in tanu.toml let you run the same tests against several environments. Arbitrary keys are available to tests through tanu::get_config():
[runner]
capture_http = "on-failure"
concurrency = 8
[[projects]]
name = "staging"
base_url = "https://staging.api.example.com"
retry.count = 3
[[projects]]
name = "production"
base_url = "https://api.example.com"
test_ignore = ["my_api_tests::destructive::delete_account"]#[tanu::test]
async fn health() -> eyre::Result<()> {
let base_url = tanu::get_config().get_str("base_url")?.to_string();
let res = Client::new().get(format!("{base_url}/health")).send().await?;
check!(res.status().is_success());
Ok(())
}Values can also come from environment variables or a .env file (TANU_API_KEY, TANU_STAGING_API_KEY). See Configuration.
| Feature | Default | Description |
|---|---|---|
native-tls |
✓ | TLS via the platform's native stack |
rustls-tls-webpki-roots |
TLS via rustls with bundled Mozilla roots (use with default-features = false) |
|
rustls-tls-native-roots |
TLS via rustls with the OS certificate store (use with default-features = false) |
|
json |
RequestBuilder::json for sending JSON bodies (Response::json is always available) |
|
cookies |
Response::cookies |
|
grpc |
gRPC call capture for tonic channels | |
graphql |
GraphQL request builder (enables json) |
[dependencies]
tanu = { version = "0.22", features = ["json", "cookies"] }The CLI runs tests and prints results in your terminal.
The TUI lets you run tests and inspect HTTP calls interactively.
Failures come with colored backtraces from color-eyre.
Test reports with tanu-allure.
| Topic | |
|---|---|
| Getting Started | Install tanu and write your first test |
| Test Attributes | #[tanu::test], parameterized tests, serial groups |
| Ordered Execution | Run a module's tests in source order |
| Assertions | check! and friends |
| gRPC / GraphQL | Protocol-specific testing |
| Configuration | tanu.toml, env vars, retries, masking |
| Command Line Options | test, tui, ls |
| Reporters | Custom reporters and Allure |
| FAQ | Common questions and troubleshooting |
As a long-time backend engineer, I wanted API tests that were fast, type-safe, and written in the same language as the services they test. The tools I tried each fell short:
- Postman is a great tool, but not designed for end-to-end API testing. It needs a GUI, assertions are written in JavaScript, and collections become huge JSON files that are hard to review and maintain.
- Playwright is excellent for web end-to-end testing and supports API testing, but I wanted to write tests in the same language as the API implementation.
- Rust's built-in
#[test]with tokio, test-case, and reqwest works, but lacks the structure needed at scale: multiple environments, request capture, retries, reporting, and a way to browse results.
tanu aims to be a dedicated framework for that job while staying plain Rust.
Issues and pull requests are welcome. To work on tanu itself:
cargo build --workspace
cargo test --workspace
cargo run -p tanu-integration-tests -- test # requires Docker (httpbin container)
mkdocs serve # preview the documentation siteSee CLAUDE.md for the full pre-PR checklist (fmt, clippy for both TLS backends, builds, integration tests) and commit conventions.
Thanks to all the amazing people who have contributed to making tanu better! Every contribution, big or small, helps build a more robust and feature-rich testing framework for the Rust community ✨
Made with contrib.rocks.
We're grateful to our sponsors who support the development of tanu:
|
yuk1ty 🐶 |
2323-code 🥩 |
Your support helps make tanu better for everyone. Thank you! 🙏
Licensed under the Apache License 2.0.