Integration of Allure Report with PyTest

Last Updated on

HOME

In this tutorial, we will discuss the Integration of Allure Report with the PyTest.

Table of Contents:

  1. What is an Allure Report?
  2. Prerequisite
  3. Implementation Steps
    1. Install allure-pytest plugin
    2. Create a new project folder and open it in PyCharm
    3. Add the allure-pytest package to the PyCharms
    4. Create the test code
    5. Execute the tests
    6. Generation of allure-results folder
    7. Generate the Allure Report
    8. Allure Report Dashboard
      1. Categories in Allure Report
      2. Suites in Allure Report
      3. Graphs in Allure Report
      4. Timeline in Allure Report
      5. Behaviours of Allure Report
      6. Packages in Allure Report

What is an Allure Report?

Allure Framework is a flexible lightweight multi-language test report tool that not only shows a very concise representation of what has been tested in a neat web report form but allows everyone participating in the development process to extract maximum useful information from everyday execution of tests.

From the dev/qa perspective, Allure reports shorten common defect lifecycle: test failures can be divided into bugs and broken tests, also logs, steps, fixtures, attachments, timings, history, and integrations with TMS and bug-tracking systems can be configured, so the responsible developers and testers will have all information at hand.

Prerequisite:

    Implementation Steps:

    Step 1 – Install allure-pytest plugin

    Go to the command prompt and run the below-mentioned command to download the plugin:

    pip install allure-pytest
    

    The below image shows that the plugin is installed successfully.

    Step 3 – Add the allure-pytest package to the PyCharms

    Go to File->Settings ->Project:PageObjectModel_Pytest->Python Interpreter.

    Click on the “+” sign and enter allure-pytest in the search bar. It will show a list of packages. Select the “allure-pytest” package and click on the “Install Package”.

    Once the package is installed, we will see the message that the package is installed successfully, and it can be seen under the package list as shown below:

    Step 4 – Create the test code

    The complete PyTest framework can be found here – Page Object Model Implementation of Python with Selenium – PyTest.

    Step 5 – Execute the tests

    We need the below command to run the PyTest Framework script using the Allure listener.

    pytest --alluredir allure-results
    

    The output of the above program is

    Step 6 – Generation of allure-results folder

    We have the test case passed. It will generate an allure-results folder in the tests directory. This folder contains all the files needed to generate the Allure Report.

    Step 7 – Generate the Allure Report

    To create Allure Report, use the below command

    allure serve allure-results
    

    This will generate the beautiful Allure Test Report as shown below.

    Step 8 – Allure Report Dashboard

    The overview page hosts several default widgets representing the basic characteristics of your project and test environment.

    1. Statistics – overall report statistics.
    2. Launches – if this report represents several test launches, statistics per launch will be shown here.
    3. Behaviours – information on results aggregated according to stories and features.
    4. Executors – information on test executors that were used to run the tests.
    5. History Trend – if tests accumulated some historical data, it’s trend will be calculated and shown on the graph.
    6. Environment – information on the test environment.

    Categories in Allure Report

    The categories tab gives you a way to create custom defect classifications to apply for test results. There are two categories of defects – Product Defects (failed tests) and Test Defects (broken tests).

    Suites in Allure Report

    On the Suites tab a standard structural representation of executed tests, grouped by suites and classes can be found.

    Graphs in Allure Report

    Graphs allow you to see different statistics collected from the test data: statuses breakdown or severity and duration diagrams.

    Timeline in Allure Report

    The timeline tab visualizes retrospective of tests execution, allure adaptors collect precise timings of tests, and here on this tab, they are arranged accordingly to their sequential or parallel timing structure.

    Behaviours of Allure Report

    This tab groups test results according to Epic, Feature, and Story tags.

    Packages in Allure Report

    The packages tab represents a tree-like layout of test results, grouped by different packages.

    That’s it! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!

    Cross Browser Testing in PyTest

    HOME

    import pytest
    from selenium import webdriver
    
    
    def pytest_addoption(parser):
        """Custom Pytest command line options"""
        parser.addoption(
            "--browser_name", action="store", default="chrome"
        )
    
    
    @pytest.fixture(scope="class")
    def driver(request):
    
        browser_name = request.config.getoption("browser_name")
    
        if browser_name == "chrome":
            options = webdriver.ChromeOptions()
            driver = webdriver.Chrome(options)
            print("Chrome Browser is opened")
        elif browser_name == "firefox":
            options = webdriver.FirefoxOptions()
            driver = webdriver.Firefox(options)
            print("Firefox Browser is opened")
        elif browser_name == "edge":
            options = webdriver.EdgeOptions()
            driver = webdriver.Edge(options)
            print("Edge Browser is opened")
        else:
            print("No browser is selected")
        driver.get("https://opensource-demo.orangehrmlive.com/")
        driver.maximize_window()
        driver.implicitly_wait(5)
        yield driver
        driver.close()
    
    
    from selenium.webdriver.common.by import By
    
    
    class Test_Login:
    
        def test_login(self,driver):
            driver.find_element(By.NAME, "username").send_keys("Admin")
            driver.find_element(By.NAME, "password").send_keys("admin123")
            driver.find_element(By.XPATH, "//*[@class='oxd-form']/div[3]/button").click()
            print("Successful login to the application")
            homePageTitle = driver.find_element(By.XPATH, "//*[@class='oxd-topbar-header-breadcrumb']/h6").text
            print("Heading of DashBoard Page: ", homePageTitle)
            assert homePageTitle == "Dashboard", "Heading is different"
    
    
    pytest test_login.py -s
    

     pytest test_login.py --browser_name firefox -s
    

    How to skip tests in PyTest – skip, skipIf, xfail

    HOME

    import pytest
    
    
    @pytest.mark.skip(reason="In Progress")
    def test_addition():
        a = 6
        b = 5
        c = 11
    
        assert a + b == 11, "Sum is not 11"
        assert a + b == c, "Sum of a and b is not equal to c"
    
    
    @pytest.mark.skipif(15-4>10, reason="Conditional")
    def test_subtraction():
        x = 15
        y = 4
        z = 10
    
        assert x - y == z, "Subtract y from x is equal to z"
    
    
    @pytest.mark.xfail
    def test_multiplication():
        a = 6
        b = 5
        c = 30
    
        assert a * b == c, "Product of 5 and 6 is not 30"
    
    
    def test_division():
        a = 16
        b = 2
        c = 8
    
        assert a / b == c, "Division of 2 by 6 is not 3"
    
    

    pytest test_skip_demo.py
    

    import pytest
    
    
    @pytest.mark.skip("Development in progress")
    class test_skip_class:
    
        def test_addition(self):
            a = 10.44
            b = 5.56
            c = 16.00
    
            assert a + b == 16.00, "Sum is not 16"
    
        def test_subtraction(self):
            x = 15
            y = 4
            z = 10
    
            assert x - y == z, "Subtract y from x is equal to z"
    
    

    Filtering by tags in PyTest-BDD

    HOME

    @Login
    Feature: Login Page
    
        @ValidCredentials
        Scenario: Successful Application Login
            Given User is on login page
            When User enter username "Admin" and password "admin123"
            Then User should be able to login successfully and new page open "Dashboard"
    

    @ScenarioOutline
    Feature: Scenario Outline Example
    
        @InvalidCredentials
        Scenario Outline: Invalid credentials generate error message
            Given User is on OrangeHome page
            When User enters username as "<username>" and password as "<password>"
            Then User should be able to see error message as "Invalid credentials"
    
          Examples:
           | username | password    |
           | admin    | admin12345  |
           | Admin123 | admin       |
           | 123      | admin       |
    
    
        @MissingUsername
        Scenario: Missing Username generates error
            Given User is on OrangeHome page
            When User enters username as " " and password as "admin123"
            Then User should be able to see error message as "Required" below the username field
    
    
    pytest
    

    [pytest]
    markers =
        Login: mark all tests as login
        ValidCredentials: mark a test as a valid credentials (deselect with '-m "not ValidCredentials"')
    	ScenarioOutline: mark all tests as ScenarioOutline
    	InvalidCredentials: mark test as invalid credentials (deselect with '-m "not InvalidCredentials"')
    	MissingUsername: mark test as missing username (deselect with '-m "not MissingUsername"')
    

     pytest -k "MissingUsername"
    

    pytest -k  "Login"
    

    pytest -m "ScenarioOutline and MissingUsername"
    

    pytest -m "ValidCredentials or MissingUsername"
    

     pytest -m "not InvalidCredentials"
    

    Scenario Outline in PyTest – BDD

    HOME

    pip install pytest
    pip install pytest-bdd
    pip install pytest-selenium (If using Selenium)
    

    Feature: Scenario Outline Example
    
        @InvalidCredentials
        Scenario Outline: Unsuccessful Application Login
            Given User is on OrangeHome page
            When User enters username as "<username>" and password as "<password>"
            Then User should be able to see error message as "Invalid credentials"
    
          Examples:
           | username | password    |
           | admin    | admin12345  |
           | Admin123 | admin       |
           | 123      | admin       |
    
    

    import pytest
    
    from pytest_bdd import scenarios, given, when, then, parsers
    from selenium import webdriver
    from selenium.webdriver.common.by import By
    
    
    # Constants
    TIMEOUT = 5
    URL = 'https://opensource-demo.orangehrmlive.com/'
    
    
    # Scenarios
    scenarios('../features/ScenarioOutline.feature')
    
    
    # Fixtures
    @pytest.fixture
    def driver():
        driver = webdriver.Firefox()
        driver.implicitly_wait(TIMEOUT)
        driver.maximize_window()
        yield driver
        driver.quit()
    
    
    # Given Steps
    @given('User is on OrangeHome page')
    def open_browser(driver):
        driver.get(URL)
    
    
    # When Steps
    @when(parsers.parse('User enters username as "{username}" and password as "{password}"'))
    def search_phrase(driver, username, password):
        driver.find_element(By.NAME, "username").send_keys(username)
        driver.find_element(By.NAME, "password").send_keys(password)
        driver.find_element(By.XPATH, "//*[@class='oxd-form']/div[3]/button").click()
    
    
    # Then Steps
    @then(parsers.parse('User should be able to see error message as "{expected_error_message}"'))
    def search_results(driver, expected_error_message):
        actual_error_message = driver.find_element(By.XPATH, "//*[@id='app']/div[1]/div/div[1]/div/div[2]/div[2]/div/div[1]/div[1]/p").text
        assert actual_error_message == expected_error_message
    

    scenarios('../features/ScenarioOutline.feature')
    

    pytest stepdefinitions/test_scenario_outline_definitions.py -s
    

     pytest stepdefinitions/test_scenario_outline_definitions.py --html=Report.html 
    

    PyTest – BDD (Behavioural Driven Development) with Selenium

    HOME

    pip install pytest
    
    pip install pytest-bdd
    

    pip install pytest-selenium
    

    Feature: Login Page
    
        Scenario: Successful Application Login
            Given User is on login page
            When User enter username "Admin" and password "admin123"
            Then User should be able to login successfully and new page open "Dashboard"
    

    import pytest
    
    from pytest_bdd import scenarios, given, when, then, parsers
    from selenium import webdriver
    from selenium.webdriver.common.by import By
    
    # Constants
    TIMEOUT = 4
    URL = 'https://opensource-demo.orangehrmlive.com/'
    
    
    # Scenarios
    scenarios('../features/Login.feature')
    
    
    # Fixtures
    @pytest.fixture
    def driver():
        driver = webdriver.Firefox()
        driver.implicitly_wait(TIMEOUT)
        driver.maximize_window()
        yield driver
        driver.quit()
    
    
    # Given Steps
    @given('User is on login page')
    def open_browser(driver):
        driver.get(URL)
    
    
    # When Steps
    @when(parsers.parse('User enter username "{username}" and password "{password}"'))
    def search_phrase(driver, username, password):
        driver.find_element(By.NAME, "username").send_keys(username)
        driver.find_element(By.NAME, "password").send_keys(password)
        driver.find_element(By.XPATH, "//*[@class='oxd-form']/div[3]/button").click()
    
    
    # Then Steps
    @then(parsers.parse('User should be able to login successfully and new page open "{heading}"'))
    def search_results(driver, heading):
        homepage_title = driver.find_element(By.XPATH, "//*[@class='oxd-topbar-header-breadcrumb']/h6").text
        assert homepage_title == heading
    
    
    scenarios('../features/Login.feature')
    

    pytest tests
    

    pytest tests --html=Report.html -s
    

    How to run Python Selenium tests with Jenkins

    HOME

    In the previous tutorial, we have seen the How to generate HTML Reports in Jenkins. In this tutorial, we show you how to integrate PyTest tests with Jenkins

    Prerequisite:

    Jenkin’s installed and started on the computer. The current Jenkins version is – 2.361.2

    Implementation Steps

    To generate HTML Report in Jenkins, we need to download HTML Publisher Plugin. Please refer to this tutorial to install the plugin – How to install Plugins in Jenkins.

    Step 1: Create a new FreeStyle project

    1. Give the Name of the projectPyTestFramework_Demo
    2. Click on the Freestyle project. 
    3. Click on the OK button.

    In the General section, enter the project description in the Description box – This is demo integration of Python with Selenium tests to run in Jenkins.

    Step 2: Select a custom workspace

    Mention the full path of the project in the Use custom workspace.

    Select Source Code Management as None if the project is locally present on the machine.

    Step 3: Build Management

    Go to the Build section of the new job. Select “Execute Windows batch command”.

    cd tests
    pytest  --html=$WORKSPACE/Reports/report.html -s
    

    Step 4: Select “Publish HTML reports” from “Post Build Actions”

    Scroll down to “Post Build Actions” and click on the “Add Post Build Actions” drop-down list. Select “Publish HTML reports“. 

    If you want to see where the report will be saved in Jenkins, go to the Dashboard -> PyTestFramework_Demo -> Workspace -> Reports ->report.html.

    Enter the HTML directory to archive – Reports, Index page[s] – report.html, and Report title – HTML Report.

    Click on the Apply and Save buttons.

    We have created a new FreeStyle project “PyTestFramework_Demo” with the configuration to run the Python tests in windows batch.

    Step 5: Execute the tests

    Let’s execute it now by clicking on the “Build Now” button. 

    Right-click on Build Number (here in my case it is #1).

    Click on Console Output to see the result.

    Step 6: View the HTML Report

    Once the execution is completed, click on go “Back to Project“, and we could see a link to view the “HTML Report“.

    We can see here that the HTML Report link is displayed in the Console.

    Below is the HTML Report generated in Jenkins.

    There are chances that the report won’t look very pretty. The reason is that CSS is stripped out because of the Content Security Policy in Jenkins.

    The default rule set in Jenkins is:

    sandbox; default-src 'none'; img-src 'self'; style-src 'self';
    

    To know more about this, please refer to this tutorial – https://www.jenkins.io/doc/book/security/configuring-content-security-policy/.

    We can customize Content Security Policy in Jenkins. But keep in mind that it should be done after checking with the Security team in your organization. This is a workaround solution. I can’t emphasize enough that this is not a standard practice.

    Go to Manage Jenkins -> Manage Nodes and Clouds.

    Click on the Script Console option.

    Type in the following command and Press Run. If you see the output as ‘Result:’ then the protection is disabled. Re-Run your build and you can see that the new HTML files archived will have the CSS enabled.

    System.setProperty("hudson.model.DirectoryBrowserSupport.CSP","")
    

    Re-run the HTMLReport_Demo project. Now you can see a properly rendered HTML Report.

    Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!! Cheers!!

    Additional Tutorials

    How to Install Python on Windows 11
    How to install and setup Robot Framework for Python
    How to rerun failed tests in Robot Framework
    How to implement tagging in Robot Framework
     How to set variable values from Runtime command in Robot Framework
    How to load data from CSV files in the Robot Framework?

    Page Object Model Implementation of Python with Selenium – PyTest

    HOME

    pip install -U pytest
    

    from selenium.webdriver import ActionChains
    from selenium.webdriver.support.wait import WebDriverWait
    from selenium.webdriver.support import expected_conditions as ec
    
    
    class BasePage:
    
        def __init__(self, driver):
            self._driver = driver
            self._wait = WebDriverWait(self._driver, 10)
            self._action = ActionChains(self._driver)
    
        def find(self, locator):
            return self._driver.find_element(*locator)
    
    

    from selenium.webdriver.common.by import By
    
    from pages.base_page import BasePage
    
    
    class LoginPage(BasePage):
        username = (By.NAME, "username")
        password = (By.NAME, "password")
        submit_button = (By.XPATH, "//*[@class='oxd-form']/div[3]/button")
        error_message = (By.XPATH, "//*[@class='orangehrm-login-error']/div[1]/div[1]/p")
    
        def execute_login(self, username: str, password: str):
            self.find(self.username).send_keys(username)
            self.find(self.password).send_keys(password)
            self.find(self.submit_button).click()
    
        def actual_error(self):
            return self.find(self.error_message).text
    
    
    from selenium.webdriver.common.by import By
    
    from pages.base_page import BasePage
    
    
    class DashboardPage(BasePage):
        heading = (By.XPATH, "//*[@class='oxd-topbar-header-breadcrumb']/h6")
        assign_leave_option = (By.XPATH, "//*[@title='Assign Leave']")
        time_at_work_option = (By.XPATH, "//*[@title='Time at Work']")
    
        def actual_heading(self):
            return self.find(self.heading).text
    
        def assign_leave_displayed(self):
            return self.find(self.assign_leave_option).is_displayed()
    
        def time_at_work_displayed(self):
            return self.find(self.assign_leave_option).is_displayed()
    
    
    import pytest
    from selenium import webdriver
    
    
    @pytest.fixture(scope="class")
    def driver():
    
        driver = webdriver.Chrome()
        driver.get("https://opensource-demo.orangehrmlive.com/")
        driver.maximize_window()
        driver.implicitly_wait(2)
        yield driver
        driver.close()
    

    import pytest
    
    from pages.dashboard_page import DashboardPage
    from pages.login_page import LoginPage
    
    
    def test_login_negative(driver, get_data):
    
        login_page = LoginPage(driver)
        login_page.execute_login(get_data["username"], get_data["password"])
        assert login_page.actual_error() == "Invalid credentials", "Error message does not match expected value"
    
    
    def test_login_positive(driver):
        login_page = LoginPage(driver)
        login_page.execute_login("Admin", "admin123")
        dashboard_page = DashboardPage(driver)
        assert dashboard_page.actual_heading() == "Dashboard", "Heading is different"
    
    @pytest.fixture(params=[{"username":"admin", "password":"admin$$"},
                            {"username":"Admin123", "password":"123"}])
    def get_data(request):
        return request.param
    

    from pages.dashboard_page import DashboardPage
    from pages.login_page import LoginPage
    
    
    def test_successful_login(driver):
    
        login_page = LoginPage(driver)
        login_page.execute_login("Admin", "admin123")
        dashboard_page = DashboardPage(driver)
        assert dashboard_page.actual_heading() == "Dashboard"
    
    
    def test_time_at_work(driver):
        login_page = LoginPage(driver)
        login_page.execute_login("Admin", "admin123")
        dashboard_page = DashboardPage(driver)
        assert dashboard_page.time_at_work_displayed()
    

    pytest 
    

     pytest --html=report.html -s
    
    

    Pytest Logging to print to a File

    HOME

    import logging
    
    
    def test_logging_file():
    
        # Create a named logger
        logger = logging.getLogger(__name__)
    
        # Create a file handler
        filehandler = logging.FileHandler('logfile.log')
    
        # Set the formatter for the file handler
        formatter = logging.Formatter("%(asctime)s : %(levelname)s : %(name)s : %(message)s")
        filehandler.setFormatter(formatter)
    
        # Add the file handler to the logger
        logger.addHandler(filehandler)  
    
        logger.setLevel(logging.INFO)
        logger.debug("Debug statement is executed")
        logger.info("Information statement is executed")
        logger.warning("Warning mode, but test continues")
        logger.error("Error has happened and failed the test")
        logger.critical("Critical issue")
    

    logging.Formatter("%(asctime)s : %(levelname)s : %(name)s : %(message)s")
    

    pytest test_logging_file.py -s
    

    import inspect
    import logging
    
    
    class BaseClass:
    
        def get_logger(self):
    
            loggerName = inspect.stack()[1][3]
            logger = logging.getLogger(loggerName)
    
            filehandler = logging.FileHandler('logfile.log')
    
            formatter = logging.Formatter("%(asctime)s : %(levelname)s : %(name)s : %(message)s")
            filehandler.setFormatter(formatter)
    
            logger.addHandler(filehandler)  
    
            logger.setLevel(logging.INFO)
            return logger
    
    
    import pytest
    from demo.BaseClass import BaseClass
    
    
    class TestExample3(BaseClass):
    
        @pytest.fixture()
        def dataload(self):
            print("User Profile data")
            return ["Vibha", "Singh", "qaautiomation.expert"]
    
        @pytest.mark.usefixtures("dataload")
        def test_user_profile(self, dataload):
            log = self.get_logger()
            log.info(dataload)
            log.info(f'First Name : {dataload[0]}')
            log.info(f'Surname : {dataload[1]}')
            log.info(f'Website : {dataload[2]}')
    
    
    pytest -s
    

    Pytest Logging to print to Console

    HOME

    import logging
    
    
    def test_logging_console():
        logger = logging.getLogger(__name__)
    
        # Create a console handler
        console_handler = logging.StreamHandler()
    
        # Set the formatter for the console handler
        formatter = logging.Formatter("%(asctime)s : %(levelname)s : %(name)s : %(message)s")
        console_handler.setFormatter(formatter)
    
        # Add the console handler to the logger
        logger.addHandler(console_handler)
        logger.setLevel(logging.INFO)
    
        logger.debug("Debug statement is executed")
        logger.info("Information statement is executed")
        logger.warning("Warning mode, but test continues")
        logger.error("Error has happened and failed the test")
        logger.critical("Critical issue")
    

    logging.Formatter("%(asctime)s : %(levelname)s : %(name)s : %(message)s")
    

    pytest test_logging_console.py -s