Of course! Here's a comprehensive guide to using ExtentReports with Python, covering everything from setup and basic usage to advanced features.

What is ExtentReports?
ExtentReports is a popular, open-source HTML reporting library for .NET and Java. The community-driven Python port allows you to generate beautiful, interactive, and comprehensive HTML reports for your automation tests. These reports are much more informative and visually appealing than standard text-based logs.
Key features of ExtentReports:
- Rich HTML Reports: Generates a single, self-contained HTML file with charts, graphs, and screenshots.
- Test Hierarchies: You can organize tests into suites, tests, and steps.
- Logging Levels: Log messages at different levels (Info, Warning, Error, Debug) with timestamps.
- Visual Indicators: Clearly shows test status with colors (Pass, Fail, Skip, Warning).
- Screenshots & Media: Easily embed screenshots, videos, or other media directly into the report.
- Interactive & Responsive: The report is interactive, allowing you to filter tests, view logs, and navigate through test steps.
- Data Attachment: You can attach custom data (like JSON, XML, or text) to test steps.
Step 1: Installation
First, you need to install the extentreports library using pip.
pip install extentreports
Step 2: A Simple "Hello World" Example
Let's start with the most basic example to see how it works.

# 1. Import the necessary classes
from extentreports import ExtentReports, ExtentHtmlReporter
from extentreports import Logger
# 2. Start the reporter and attach it to the ExtentReports instance
# This will create the HTML file.
html_reporter = ExtentHtmlReporter("MyFirstReport.html")
# 3. Create an instance of ExtentReports
extent = ExtentReports()
# 4. Attach the HTML reporter to the ExtentReports
extent.attach_reporter(html_reporter)
# 5. Create a logger for our test
logger = extent.create_test("Test Case 1: Simple Verification")
# 6. Log information during the test
logger.pass("This step passed!")
logger.info("An informational message.")
logger.warning("This is just a warning.")
logger.fail("This step failed!")
# 7. Flush the report to write everything to the file
extent.flush()
print("Report generated successfully!")
After running this code, you will find a MyFirstReport.html file in your project directory. Open it in a browser to see your first report.
Step 3: Detailed Breakdown of the Code
Let's break down the key components from the example above.
ExtentHtmlReporter
This class is responsible for creating the HTML file. You provide it with a file path.
from extentreports import ExtentHtmlReporter
# This creates the HTML file and sets up the report structure
html_reporter = ExtentHtmlReporter("MyReport.html")
You can also configure the ExtentHtmlReporter:

html_reporter.config().set_theme(Theme.STANDARD) # Set theme (STANDARD, DARK)
html_reporter.config().set_report_name("My Automation Test Suite")
html_reporter.config().set_document_title("Regression Test Results")
ExtentReports
This is the main class that orchestrates the entire reporting process. It manages the reporters (like ExtentHtmlReporter) and the test logs.
from extentreports import ExtentReports # Create an instance of the main report object extent = ExtentReports() # Attach the HTML reporter to the main report object extent.attach_reporter(html_reporter)
Logger
A logger represents a single test case. You get a logger by calling extent.create_test().
from extentreports import Logger
# Create a logger for a specific test case
# You can give it a name and optionally a description
logger = extent.create_test("Login Test", "Verifies that a user can log in successfully")
Logging Methods
The logger object has various methods to log the status of your test steps.
| Method | Description | Example |
|---|---|---|
logger.pass(message) |
Logs a passed step. The step is highlighted in green. | logger.pass("Login button was clicked successfully.") |
logger.fail(message) |
Logs a failed step. The step is highlighted in red. | logger.fail("Username field was not found.") |
logger.skip(message) |
Logs a skipped step. The step is highlighted in grey. | logger.skip("Skipping mobile view test as device is not available.") |
logger.warning(message) |
Logs a warning. The step is highlighted in yellow. | logger.warning("Page load time was slow.") |
logger.info(message) |
Logs an informational message. | logger.info("Navigating to the login page.") |
logger.debug(message) |
Logs a debug message. | logger.debug("Element 'username' located by ID.") |
extent.flush()
This is a crucial step. It finalizes the report and writes all the logs and data to the HTML file. Without it, your report will be empty.
# Always call flush() at the end of your script extent.flush()
Step 4: Integrating with a Test Runner (Pytest)
The real power of ExtentReports shines when you integrate it with a test runner like pytest. Here’s a practical example.
Project Structure:
my_project/
├── pytest.ini
├── requirements.txt
├── reports/
│ └── report.html
└── tests/
├── __init__.py
├── conftest.py
└── test_login.py
requirements.txt
pytest
selenium
extentreports
conftest.py (Pytest Fixture)
This file will set up the ExtentReports instance and a logger for each test. Pytest's pytest_runtest_makereport hook is used to capture the outcome of each test.
# conftest.py
import pytest
from extentreports import ExtentReports, ExtentHtmlReporter, Logger
from selenium import webdriver
# --- ExtentReports Setup ---
@pytest.fixture(scope="session", autouse=True)
def setup_reports():
# Create the reports directory if it doesn't exist
import os
report_dir = os.path.join(os.path.dirname(__file__), "..", "reports")
os.makedirs(report_dir, exist_ok=True)
# Initialize the HTML reporter
report_path = os.path.join(report_dir, "report.html")
html_reporter = ExtentHtmlReporter(report_path)
html_reporter.config().set_report_name("My Pytest Report")
html_reporter.config().set_document_title("Automation Results")
# Initialize the main ExtentReports object
global extent
extent = ExtentReports()
extent.attach_reporter(html_reporter)
yield # This allows the tests to run
# Flush the report after all tests are done
extent.flush()
# --- Pytest Hook to capture test results ---
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
# This function runs for each test case
outcome = yield
report = outcome.get_result()
# Get the logger associated with the current test
# We need to store the logger on the item itself in the runtest_setup phase
if report.when == "setup":
logger = extent.create_test(report.nodeid)
item.user_properties.append(("logger", logger))
# Get the logger from the item's properties
logger = None
for name, value in item.user_properties:
if name == "logger":
logger = value
break
if logger:
if report.when == "call":
if report.failed:
logger.fail(f"Test Failed: {report.longrepr}")
# You can add screenshots here if you have a driver instance
elif report.skipped:
logger.skip(f"Test Skipped: {report.longrepr}")
else:
logger.pass("Test Passed!")
elif report.when == "teardown" and report.failed:
logger.fail("Test setup/teardown failed.")
tests/test_login.py (Example Test File)
# tests/test_login.py
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# Fixture to provide a WebDriver instance to each test
@pytest.fixture
def driver():
driver = webdriver.Chrome()
driver.implicitly_wait(10)
yield driver
driver.quit()
def test_successful_login(driver):
# Get the logger from the pytest item (set up in conftest.py)
logger = pytest.current_node().user_properties[0][1]
logger.info("Starting test: Successful Login")
logger.info("Navigating to login page")
driver.get("https://the-internet.herokuapp.com/login")
logger.info("Entering username and password")
driver.find_element(By.ID, "username").send 