Skip to content

Allure Reporting: Rich Test Reports for API Test Results

DodaTech Updated 2026-06-28 6 min read

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

  1. What is Allure Framework and why use it for test reporting?
  2. How do you attach HTTP request/response data to Allure reports?
  3. What do Allure annotations (epic, feature, story, severity) do?
  4. How do you preserve test history across Allure runs?

Answers:

  1. 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.
  2. 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.
  3. 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.
  4. Copy allure-report/history to allure-results/history before 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

How do I integrate Allure with pytest?

Install allure-pytest. Run tests with --alluredir=allure-results. Generate report with allure generate allure-results. Allure-pytest automatically captures test names, status, and durations.

Can I customize the Allure report logo and colors?

Yes, create allure-report/config.yml in the report directory. Override theme colors, logo URL, and report title. Custom CSS can also be injected.

How do I filter tests by severity in Allure?

Allure report UI has filter controls. Select severity level from the dropdown. You can also use the API: allure-report/index.html#severity=BLOCKER.

What is the difference between Allure and pytest-html?

pytest-html generates a simple static HTML table. Allure is interactive: filtering, search, trends, attachments, environment info, and historical comparison. Allure is more powerful but requires more setup.

Can Allure generate reports for load tests?

Yes, Allure supports any test framework. For k6, use the Allure plugin for k6 or convert k6 JSON output to Allure-compatible format. Results appear with duration histograms.

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