Allure Reporting: Rich Test Reports for API Test Results
In this tutorial, you will learn about Allure Reporting: Rich Test Reports for API Test Results. We cover key concepts, practical examples, and best practices to help you master this topic.
Allure Framework generates interactive test reports with rich visualizations, test history, trends, attachments (request/response logs, screenshots), and environment information, making API test results actionable and understandable.
What You'll Learn
How to install and configure Allure, annotate tests with Allure decorators (features, stories, severity), attach HTTP request/response data to reports, generate trend history across test runs, integrate with CI/CD, and customize dashboards.
Why It Matters
Plain JUnit XML reports are hard to navigate. Allure provides an interactive web interface with filtering, search, trends, and attachments. DodaTech uses Allure dashboards to track test health trends across 2,000+ API tests per deployment.
Real-World Use
A QA manager opens the Allure dashboard for the latest deployment: 1,950 passed, 23 failed, 27 broken. They filter by severity=blocker, see 2 failures, click to view the failed request/response logs, and assign bugs to the development team.
flowchart LR
A["Test\nRun"] --> B["Allure\nResults (JSON)"]
B --> C["allure generate\nCommand"]
C --> D["HTML\nReport"]
D --> E["Dashboard:\nOverview"]
D --> F["Suites:\nTest Details"]
D --> G["Graphs:\nTrends"]
D --> H["Timeline:\nExecution Order"]
style A fill:#dbeafe,stroke:#2563eb
style D fill:#bbf7d0,stroke:#16a34a
Installing Allure
# Install Allure CLI
# macOS:
brew install allure
# Linux:
# Download from https://github.com/allure-framework/allure2/releases
# Add allure/bin to PATH
# Python package (for pytest integration)
pip install allure-pytest
# Node.js package (for Mocha/Jest)
npm install --save-dev allure-mocha
# Java package (for REST Assured/JUnit)
# Gradle: testCompile 'io.qameta.allure:allure-junit5:2.27.0'
# Maven: add allure-junit5 dependency
Allure Annotations (Python/Pytest)
import pytest
import allure
@allure.epic("User Management")
@allure.feature("User CRUD")
@allure.story("Create User")
@allure.severity(allure.severity_level.CRITICAL)
@allure.tag("smoke", "regression")
@allure.parent_suite("API Tests")
@allure.suite("Users Module")
def test_create_user(api_client):
"""Create a new user with valid data."""
with allure.step("Prepare test data"):
user_data = {
"email": "allure-test@example.com",
"name": "Allure Test User",
"password": "SecurePass123!"
}
with allure.step("Send POST request to /users"):
response = api_client.post("/users", json=user_data)
with allure.step("Verify response status is 201"):
assert response.status_code == 201
with allure.step("Verify user was created correctly"):
data = response.json()
assert data["email"] == user_data["email"]
assert "id" in data
allure.attach(
response.text,
name="Response Body",
attachment_type=allure.attachment_type.JSON
)
@allure.feature("User CRUD")
@allure.story("Get User")
@allure.severity(allure.severity_level.NORMAL)
@pytest.mark.parametrize("user_id, expected_status", [
(1, 200),
(99999, 404),
(-1, 400),
])
def test_get_user_various(api_client, user_id, expected_status):
response = api_client.get(f"/users/{user_id}")
with allure.step(f"Verify status is {expected_status}"):
assert response.status_code == expected_status
allure.attach(
f"Request: GET /users/{user_id}",
name="Request Info",
attachment_type=allure.attachment_type.TEXT
)
Allure with REST Assured (Java)
import io.qameta.allure.*;
import org.junit.jupiter.api.Test;
@Epic("Payment Processing")
@Feature("Stripe Payment")
public class PaymentApiTest {
@Test
@Story("Successful Payment")
@Severity(SeverityLevel.BLOCKER)
@Description("Process a valid payment through Stripe integration")
@Link(name = "Stripe Docs", url = "https://stripe.com/docs/api")
@Owner("Payment Team")
public void testCreatePayment() {
// REST Assured test with Allure integration
given()
.contentType(ContentType.JSON)
.body("{\"amount\": 2999, \"currency\": \"usd\"}")
.when()
.post("/api/payments")
.then()
.statusCode(200);
}
}
Attaching HTTP Data in Allure
import allure
import json
class AllureApiClient:
"""API client that attaches all HTTP data to Allure reports."""
def _attach_request(self, method, url, kwargs):
request_info = f"{method} {url}\n"
if "json" in kwargs:
request_info += f"Body: {json.dumps(kwargs['json'], indent=2)}\n"
if "headers" in kwargs:
request_info += f"Headers: {json.dumps(dict(kwargs['headers']), indent=2)}\n"
allure.attach(
request_info,
name=f"Request: {method} {url}",
attachment_type=allure.attachment_type.TEXT
)
def _attach_response(self, response):
response_info = (
f"Status: {response.status_code}\n"
f"Time: {response.elapsed.total_seconds()*1000:.0f}ms\n"
f"Headers: {json.dumps(dict(response.headers), indent=2)}\n"
)
allure.attach(
response_info,
name=f"Response: {response.status_code}",
attachment_type=allure.attachment_type.TEXT
)
try:
allure.attach(
response.text,
name="Response Body",
attachment_type=allure.attachment_type.JSON
)
except Exception:
allure.attach(
response.text,
name="Response Body",
attachment_type=allure.attachment_type.TEXT
)
def request(self, method, url, **kwargs):
self._attach_request(method, url, kwargs)
response = super().request(method, url, **kwargs)
self._attach_response(response)
return response
Generating and Viewing Reports
# Run tests with Allure output
pytest tests/ \
--alluredir=allure-results \
--clean-alluredir \
-v
# Generate HTML report
allure generate allure-results -o allure-report --clean
# Open report in browser
allure open allure-report
# Expected output:
# Report successfully generated to allure-report
# Starting web server at http://localhost:5678
# CI integration - serve reports
allure generate allure-results -o allure-report
# Deploy allure-report directory to static hosting (S3, Netlify, GitHub Pages)
# Or use Allure Server (Docker):
# docker run -p 5050:5050 \
# -v $(pwd)/allure-results:/app/allure-results \
# frankescobar/allure-docker-service
Allure Environment and History
# environment.properties - add to allure-results directory
# Creates environment info banner in report
"""
Environment.Staging
Browser.Chrome
API.Version.v2
Database.PostgreSQL 16
Deployment.Kubernetes
"""
# categories.json - create custom failure categories
# Place in allure-results directory
"""
[
{
"name": "Ignored tests",
"messageRegex": ".*ignored.*",
"matchedStatuses": ["skipped"]
},
{
"name": "Infrastructure problems",
"messageRegex": ".*ConnectionError.*",
"matchedStatuses": ["broken"]
},
{
"name": "Assertion failures",
"messageRegex": ".*AssertionError.*",
"matchedStatuses": ["failed"]
},
{
"name": "Test defects",
"messageRegex": ".*",
"matchedStatuses": ["broken"]
}
]
"""
# Keep history between runs
# Copy allure-report/history to allure-results/history before generation
# import shutil
# shutil.copytree("allure-report/history", "allure-results/history", dirs_exist_ok=True)
Common Mistakes
1. Not Adding Attachments
Allure reports without request/response data are not useful for debugging failures. Always attach HTTP method, URL, request body, response status, body, and headers to every API test.
2. Skipping Test Annotations
Without @allure.feature, @allure.story, and @allure.severity, the report lacks structure. Tests appear as a flat list. Annotations enable filtering, grouping, and severity-based triage.
3. Forgetting --clean-alluredir
Running tests without --clean-alluredir appends results to previous runs. Old results mix with new ones, showing outdated tests. Always clean before fresh runs in CI.
4. Not Preserving History
Without history, each report is a snapshot. Trends across builds require copying history directory between runs. Set up a CI step to persist and restore history.
5. Ignoring Categories
Default categories group all failures as "Test defects". Custom categories distinguish infrastructure issues (connection errors) from test bugs (assertion failures) from product bugs (API returns wrong data).
Practice Questions
- What is Allure Framework and why use it for test reporting?
- How do you attach HTTP request/response data to Allure reports?
- What do Allure annotations (epic, feature, story, severity) do?
- How do you preserve test history across Allure runs?
Answers:
- Allure generates interactive HTML test reports with filtering, search, trends, attachments, and environment info. It's more actionable than JUnit XML because you can browse by feature, severity, or status.
- Use
allure.attach(data, name, attachment_type)in test code. Attach request method/URL/body before the call, and response status/body/headers after. In API client wrappers, add attachment calls to every request. - epic = highest-level grouping (module), feature = feature area (User Management), story = specific scenario (Create User), severity = importance (BLOCKER, CRITICAL, NORMAL, MINOR, TRIVIAL). They enable hierarchical filtering in the report.
- Copy
allure-report/historytoallure-results/historybefore generating a new report. In CI: download history artifact from previous build, place in allure-results, generate report, upload new history artifact.
Challenge: Set up Allure reporting for an API test suite: add annotations to all tests (epic, feature, story, severity), implement attachment of all HTTP data, configure environment.properties and categories.json, set up CI pipeline with history preservation, generate trend reports across 5 builds, and customize the dashboard.
FAQ
Mini Project
Set up complete Allure reporting for an API test suite: install Allure CLI and pytest plugin, annotate 10 tests with epic/feature/story/severity, implement HTTP attachment helper, configure environment.properties and categories.json, set up CI pipeline with history preservation, generate trend view across 3 mock builds, and customize the dashboard.
What's Next
Complete Testing Project — build a comprehensive API test automation project.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro