Skip to content

Nested steps

Vadzim Hushchanskou edited this page Apr 12, 2022 · 8 revisions

What is Nested Steps

Nested steps is a common way to group your test logs into small described pieces. Here is how one of our internal test looks like: nested steps

Let's imagine we have a test for some products ordering flow:

import logging

from web import OrderingSimulator

logging.basicConfig(level=logging.INFO)

logger = logging.getLogger(__name__)

order_simulator = OrderingSimulator()


def test_order_products():
    product_count = 5
    price = 3.0
    total_price = price * product_count

    logger.info('Main page displayed')

    order_simulator.log_in()
    logger.info('User logged in')

    products = order_simulator.get_products()
    logger.info('Products page opened')

    product = order_simulator.choose_product()
    logger.info("Product click event")

    logger.info(str(product_count) + " products selected")

    order_simulator.add_product(product, product_count)
    logger.info(str(product_count) + " products added to the cart")
    assert 5 == product_count

    order_simulator.do_payment(total_price)
    logger.info("Successful payment")

    order_simulator.log_out()
    logger.info("User logged out")

After running this method with our listener we have next results on the Report Portal page: no step organization

Pretty much stuff with different logic is included in the one single test function. So we can move different operations to separate functions:

import logging

from web import OrderingSimulator

logging.basicConfig(level=logging.INFO)

logger = logging.getLogger(__name__)

order_simulator = OrderingSimulator()


def navigate_to_main_page():
    logger.info('Main page displayed')


def login():
    order_simulator.log_in()
    logger.info('User logged in')


def navigate_to_products_page():
    products = order_simulator.get_products()
    logger.info('Products page opened')
    return products


def click_on_product():
    product = order_simulator.choose_product()
    logger.info("Product click event")
    return product


def select_products_count(count):
    logger.info(str(count) + " products selected")


def click_cart_button(product, count):
    order_simulator.add_product(product, count)
    logger.info(str(count) + " products added to the cart")
    assert 5 == count


def add_product_to_cart(product_count):
    product = click_on_product()
    select_products_count(product_count)
    click_cart_button(product, product_count)


def pay(total_price):
    order_simulator.do_payment(total_price)
    logger.info("Successful payment")


def logout():
    order_simulator.log_out()
    logger.info("User logged out")


def test_order_products():
    product_count = 5
    price = 3.0
    total_price = price * product_count

    navigate_to_main_page()
    login()
    navigate_to_products_page()
    add_product_to_cart(product_count)
    pay(total_price)
    logout()

Much better, but result on the Report Portal looks the same. So we grouped our logic by functions, but we cannot see our grouping on the view. That's a problem. And we can solve it using @step decorator.

In Report Portal Step is a TestItem without statistics that required for splitting large test functions or methods on multiple parts to provide clear and concise view for them. Steps have flexible structure and can be put under other Steps. @step decorator consists of 4 args:

def step(name_source, params=None, status='PASSED', rp_client=None):
    """Nested step report function.

    Create a Nested Step inside a test method on Report Portal.
    :param name_source: a function or string which will be used as step's name
    :param params:      nested step parameters which will be reported as the
                        first step entry. If 'name_source' is a function
                        reference and this parameter is not specified, they
                        will be taken from the function.
    :param status:      the status which will be reported after the step
                        passed. Can be any of legal Report Portal statuses.
                        E.G.: PASSED, WARN, INFO, etc. Default value is PASSED
    :param rp_client:   overrides Report Portal client which will be used in
                        step reporting
    :return: a step context object
    """
    pass

So now we can update our test with @step decorators and get a view that matches with our grouping:

import logging

from reportportal_client import step

from web import OrderingSimulator

logging.basicConfig(level=logging.INFO)

logger = logging.getLogger(__name__)

order_simulator = OrderingSimulator()


@step
def navigate_to_main_page():
    logger.info('Main page displayed')


@step
def login():
    order_simulator.log_in()
    logger.info('User logged in')


@step
def navigate_to_products_page():
    products = order_simulator.get_products()
    logger.info('Products page opened')
    return products


@step
def click_on_product():
    product = order_simulator.choose_product()
    logger.info("Product click event")
    return product


@step
def select_products_count(count):
    logger.info(str(count) + " products selected")


@step
def click_cart_button(product, count):
    order_simulator.add_product(product, count)
    logger.info(str(count) + " products added to the cart")
    assert 5 == count


@step
def add_product_to_cart(product_count):
    product = click_on_product()
    select_products_count(product_count)
    click_cart_button(product, product_count)


@step
def pay(total_price):
    order_simulator.do_payment(total_price)
    logger.info("Successful payment")


@step
def logout():
    order_simulator.log_out()
    logger.info("User logged out")


def test_order_products():
    product_count = 5
    price = 3.0
    total_price = price * product_count

    navigate_to_main_page()
    login()
    navigate_to_products_page()
    add_product_to_cart(product_count)
    pay(total_price)
    logout()

Results on the Report Portal page:

Nested steps

Clone this wiki locally