Fading Coder

An Old Coder’s Final Dance

Home > Tech > Content

Documenting Test Workflows in pytest with Allure Steps

Tech 2

Alllure’s step API is an effective way to describe multi-step test flows in pytest, improving readability and making reports actionable. This guide shows how to:

  • Add step annotations to linear flows
  • Nest steps and reuse them across modules
  • Use placeholders to inject arguments in to step titles
  • Record steps executed inside fixtures (from conftest.py)

Linear workflow with step annotations

Example scenario: sign in, browse catalog, add to cart, submit order, pay.

# file: test_allure_steps_flow.py

import pytest
import allure


@allure.step("Sign in to the application")
def sign_in():
    print("Signing in...")


@allure.step("Browse the product catalog")
def browse_catalog():
    print("Browsing catalog...")


@allure.step("Add SKU={sku} (qty={qty}) to cart")
def add_to_cart(sku: str, qty: int = 1):
    print(f"Adding {qty} x {sku} to the cart")


@allure.step("Create a new order")
def create_order():
    print("Creating order...")


@allure.step("Pay for order using {method}")
def pay_for_order(method: str = "card"):
    print(f"Paying with {method}")


def test_purchase_happy_path():
    sign_in()
    browse_catalog()
    add_to_cart("BOOK-123", qty=2)
    create_order()
    pay_for_order(method="wallet")

    with allure.step("Assert order submission succeeded"):
        assert True


if __name__ == "__main__":
    pytest.main(["-s", "test_allure_steps_flow.py"])

Run and open the report:

pytest test_allure_steps_flow.py --alluredir=./report/result_data
allure serve ./report/result_data

Nested steps and importing steps from another module

Steps can call other steps, and can be orgenized in separate modules for reuse.

# file: helpers.py

import allure


@allure.step("External helper step 02")
def step_two():
    print("executing step_two")
# file: test_allure_steps_nested.py

import pytest
import allure
from helpers import step_two


@allure.step("Helper step 01")
def step_one():
    print("executing step_one")


@allure.step("Composite step: invoke nested steps")
def composite_step():
    inner_step()


@allure.step("Inner step")
def inner_step():
    inner_with_args(7, "xyz")


@allure.step("Inner with args x={x}, y={y}")
def inner_with_args(x, y):
    print(f"x={x}, y={y}")


def test_imported_step_usage():
    step_one()
    step_two()


def test_nested_steps():
    step_one()
    composite_step()
    step_two()


if __name__ == "__main__":
    pytest.main(["-s", "test_allure_steps_nested.py"])

Run and open the report:

pytest test_allure_steps_nested.py --alluredir=./report/result_data
allure serve ./report/result_data

In the report, nested calls appear as a hierarchical tree under the parent step. Importing from another module preserves the step structure and title.

Step titles with placeholders and parameter injection

Allure allows formatting step titles using function arguments. You can combine positional and keyword placeholders.

# file: test_allure_steps_with_placeholders.py

import pytest
import allure


@allure.step('Prepare dataset run={0} for user="{user_id}" in mode="{mode}"')
def prepare_dataset(run_id: int, user_id: str = "guest", mode: str | None = None):
    print(f"run={run_id}, user={user_id}, mode={mode}")


def test_step_placeholders():
    prepare_dataset(1, user_id="alice", mode="fast")
    prepare_dataset(2)  # uses defaults
    prepare_dataset(3, mode="slow")


if __name__ == "__main__":
    pytest.main(["-s", "test_allure_steps_with_placeholders.py"])

Run and open the report:

pytest test_allure_steps_with_placeholders.py --alluredir=./report/result_data
allure serve ./report/result_data

The rendered step titles include argument values injected through the placeholders.

Steps inside fixtures defined in confetst.py

You can place steps in fixtures so they show up in the Setup and Teardown sections of the report.

# file: conftest.py

import pytest
import allure


@pytest.fixture()
def env_fixture():
    setup_environment()
    yield
    cleanup_environment()


@allure.step("Setup: initialize test environment")
def setup_environment():
    print("env setup")


@allure.step("Teardown: cleanup test environment")
def cleanup_environment():
    print("env cleanup")
# file: test_allure_steps_in_fixture.py

import pytest
import allure


@allure.step("Execute main test step")
def main_step():
    print("main step")


def test_uses_env_fixture(env_fixture):
    main_step()


if __name__ == "__main__":
    pytest.main(["-s", "test_allure_steps_in_fixture.py"])

Run and open the report:

pytest test_allure_steps_in_fixture.py --alluredir=./report/result_data
allure serve ./report/result_data

Steps executed within fixtures will be grouped under Setup and Teardown in the test’s report node.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.