Rust Cargo Test reference
These are the main building blocks you can use to integrate Rust tests with Allure by using allure-cargotest.
Macros
#[allure_test]
Supported forms:
#[allure_test]#[allure_test(name = "Login works")]#[allure_test(id = "AUTH-1")]#[allure_test(doc = false)]#[allure_test(name = "Login works", id = "AUTH-1")]
Use the macro together with #[test]:
use allure_cargotest::allure_test;
#[allure_test(name = "Login works", id = "AUTH-1")]
#[test]
fn login_works() {
allure.feature("Authentication");
allure.story("Login with username and password");
}What the macro does:
- initializes the reporter by using
ALLURE_RESULTS_DIRortarget/allure-results, - injects an
allurefacade into the test body, - starts and stops the test lifecycle automatically,
- applies the default labels described in Configuration,
- derives suite labels from
module_path!(), - uses the function's Rust doc comment as the default markdown description, unless
doc = falseis set ordescription(...)is called in the test body.
Behavior notes:
#[allure_test]supports both synchronous functions andasync fn; compose it with a runtime-specific test macro such as#[tokio::test]placed below it (allure-cargotestdoes not depend on Tokio itself),- besides
(), test functions may returnResult<T, E>(whereTis itself a supported return type),ExitCode, or any other type implementingstd::process::Termination;Errvalues and unsuccessful termination values are reported to Allure before Cargo interprets the result, #[should_panic]is supported only for tests that return(),#[should_panic(expected = "...")]marks the test as passed only when the panic message contains the expected substring,- a panic inside the test body is always reported
failed, regardless of its message —brokenis only produced by a returnedResult::Err, an unsuccessfulExitCode, or an unsuccessful customTerminationvalue (see the return-type bullet above), never by panicking.
#[step]
Supported forms:
#[step]#[step(name = "Open login page")]
Use #[step] on helper functions that you want to render as steps in the report:
use allure_cargotest::{allure_test, step};
#[step(name = "Open login page")]
fn open_login_page() {
// ...
}
#[allure_test]
#[test]
fn login_works() {
open_login_page();
}When the function runs inside an active Allure test, the integration starts and stops a step automatically. Outside an active Allure context, the function behaves like a normal Rust function.
Runtime facade API
Inside #[allure_test], the allure facade provides methods for the most common reporting tasks.
Metadata and labels
allure.description(text)allure.description_html(html)allure.label(name, value)allure.labels([(name, value), ...])allure.owner(value)allure.severity(value)allure.layer(value)allure.tag(value)allure.tags(["smoke", "auth"])allure.id(value)
Example:
use allure_cargotest::allure_test;
#[allure_test]
#[test]
fn login_works() {
allure.description("Checks that a valid user can sign in.");
allure.owner("John Doe");
allure.severity("critical");
allure.label("microservice", "ui");
allure.tags(["smoke", "auth"]);
}Identity and display
allure.display_name(name)— overrides the name shown in the report, independent of#[allure_test(name = "...")], for when the display name has to be computed at runtimeallure.history_id(value)— overrides the identifier Allure uses to match this result against previous runs for retry/flaky/history tracking (by default derived from the full test name and non-excluded parameters)allure.test_case_id(value)— overrides the identifier Allure uses to group results into one logical test case across environments (by default derived from the full test name)
Example:
use allure_cargotest::allure_test;
#[allure_test]
#[test]
fn login_works() {
allure.display_name("Login works (computed at runtime)");
allure.history_id("login-works-stable-id");
allure.test_case_id("AUTH-LOGIN-001");
}Hierarchies
allure.epic(value)allure.feature(value)allure.story(value)allure.parent_suite(value)allure.suite(value)allure.sub_suite(value)
Example:
use allure_cargotest::allure_test;
#[allure_test]
#[test]
fn login_works() {
allure.epic("Web interface");
allure.feature("Authentication");
allure.story("Login with username and password");
allure.parent_suite("UI tests");
allure.suite("Authentication");
allure.sub_suite("Positive scenarios");
}Links and parameters
allure.link(url, Some(name), Some(link_type))allure.links([(url, Some(name), Some(link_type)), ...])allure.issue(name, url)allure.tms(name, url)allure.parameter(name, value)allure.parameter_excluded(name, value, excluded)—excluded: truekeeps the parameter visible in the report without letting it affect the history/retry identity computed from parametersallure.parameter_mode(name, value, mode)— aParameterModefromallure_rust_commons:Maskedshows the name but hides the value (for secrets),Hiddenremoves the parameter from the report entirely,Defaultis the normal displayallure.parameter_with_options(name, value, excluded, mode)— combines both controls in one call
Example:
use allure_cargotest::allure_test;
use allure_rust_commons::ParameterMode;
#[allure_test]
#[test]
fn login_works() {
allure.issue("AUTH-123", "https://jira.example.com/browse/AUTH-123");
allure.tms("TMS-456", "https://tms.example.com/cases/TMS-456");
allure.parameter("browser", "firefox");
allure.parameter_mode("password", "hunter2", ParameterMode::Masked);
allure.parameter_excluded("sessionToken", "zzz-123", true);
}Attachments
allure.attachment(name, content_type, body)allure.attachment_path(name, content_type, path)— reads the attachment body from a fileallure.attach_trace(path)/allure.attach_trace_named(name, path)— attaches an existing Playwright trace archive (a convenience wrapper aroundattachment_pathusing theapplication/vnd.allure.playwright-tracecontent type, so Allure's trace viewer opens it instead of offering a plain zip download;attach_tracedefaults the attachment name totrace.zip) — does not generate traces or depend on Playwright itself
Example:
use allure_cargotest::allure_test;
#[allure_test]
#[test]
fn login_works() {
allure.attachment(
"response.json",
"application/json",
br#"{"status":"ok","user":"demo"}"#,
);
allure
.attachment_path("server.log", "text/plain", "assets/server.log")
.expect("failed to read the log file");
}Run-level (global) diagnostics
These attach evidence to the whole test run (the report's launch/Environment level) instead of the current test. They can be called from inside #[allure_test], or on their own even when no test is currently active:
allure.global_attachment(name, content_type, body)allure.global_attachment_path(name, content_type, path)allure.global_error(message)allure.global_error_with_trace(message, trace)
Example:
use allure_cargotest::allure_test;
#[allure_test]
#[test]
fn login_works() {
allure
.global_attachment("run log", "text/plain", "shared setup output")
.expect("failed to write the run-level attachment");
}Steps
allure.step(name, || { ... })runs a closure as a step and returns its valueallure.enter_step(name)returns aStepGuardthat keeps the step open until droppedallure.log_step(name)allure.log_step_with(name, status, error)
Examples:
use allure_cargotest::{allure_test, Status};
#[allure_test]
#[test]
fn login_works() {
allure.step("Open login page", || {
// ...
});
let mut guard = allure.enter_step("Submit credentials");
// ...
drop(guard);
allure.log_step("Verify the page title");
allure.log_step_with("Check audit log", Some(Status::Failed), Some("entry not found"));
}StepGuard also lets you override the final step status before the guard is dropped, with fail or the more general set_status:
use allure_cargotest::allure_test;
#[allure_test]
#[test]
fn login_works() {
let mut guard = allure.enter_step("Validate response");
guard.fail("Unexpected status code");
}Stages
allure.stage(name)opens a new named "stage" step. Unlikestep/enter_step, you don't close a stage explicitly — starting the next stage (or ending the test) automatically closes the previous one as passed. Anything recorded in between (steps, attachments, logged assertions) nests under whichever stage is currently open.
Example:
use allure_cargotest::allure_test;
#[allure_test]
#[test]
fn login_works() {
allure.stage("open login page");
allure.log_step("login page opened");
allure.stage("collect evidence");
allure.attachment("page.html", "text/html", "<html>...</html>");
}This produces two top-level steps — open login page (containing login page opened) and collect evidence (containing the page.html attachment) — without manually nesting closures.
Manual integration with CargoTestReporter
If macros are not enough for your test harness, you can use CargoTestReporter directly:
use allure_cargotest::CargoTestReporter;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let reporter = CargoTestReporter::new("target/allure-results")?;
reporter.run_test("login_works", |allure| {
allure.feature("Authentication");
allure.parameter("browser", "firefox");
});
Ok(())
}Useful methods:
CargoTestReporter::new(results_dir)run_test(name, |allure| { ... })run_test_with_metadata(test_name, full_name, allure_id, tags, |allure| { ... })run_test_with_result(name, |allure| { ... })is_selected(test_name, full_name, allure_id, tags)
run_test_with_metadata and is_selected are the only entry points that forward an explicit allure_id/tags pair into test-plan matching, so id entries in an ALLURE_TESTPLAN_PATH file only take effect for integrations built on CargoTestReporter directly — not for #[allure_test(id = "...")], which currently only participates in selector matching.
Building a custom integration with allure-rust-commons
Use allure-rust-commons when you need low-level control over the lifecycle:
use allure_rust_commons::{
AllureRuntime, FileSystemResultsWriter, StartTestCaseParams, Status,
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let writer = FileSystemResultsWriter::new("target/allure-results")?;
let runtime = AllureRuntime::new(writer);
let lifecycle = runtime.lifecycle();
lifecycle.start_test_case(
StartTestCaseParams::new("login_works").with_full_name("auth::login_works"),
);
lifecycle.stop_test_case(Status::Passed, None);
Ok(())
}The main low-level types are:
AllureRuntimeAllureLifecycleStartTestCaseParamsFileSystemResultsWriterStatusandStatusDetails- the model types exported from
allure_rust_commons::model