Aliaksandr Plekhau
Aliaksandr Plekhau

Reputation: 493

Pytest: best practices for logging using allure

Right now I’m trying to figure out the best way of logging for pytest. I’m going to use allure for that. It is important for me to log all functions calls where each contains:

For example, I can do it like this:

import allure


@allure.step("The sum of two numbers")
def my_sum(a, b):
    result = a + b
    with allure.step("Result: {}".format(result)):
        pass
    return result


def test_sum():
    observed = my_sum(1, 2)
    assert observed == 3

Log:

enter image description here

Log looks good for me, but code looks ugly.

Second way:

import allure


@allure.step("The sum of two numbers")
def my_sum(a, b):
    result = a + b
    allure.attach(str(result), 'Result', allure.attachment_type.TEXT)
    return result


def test_sum():
    observed = my_sum(1, 2)
    assert observed == 3

Log:

enter image description here

Log looks a little bit worse, but it’s ok. In this case, for each attachment will be created separate file. I am afraid that a huge number of attachments will be created for a large log and report will work slowly.

Also in both cases I need to add a lot of code for each function + adding timestamps to my code looks ugly.

Please share your best practices for logging using allure.

P.S. Before I worked with Robot Framework and code above in this framework looks like:

*** Settings ***


*** Test Cases ***
Test Sum
    ${observed}    My Sum    1    2
    Should Be Equal As Strings    ${observed}    3


*** Keywords ***
My Sum
    [Arguments]    ${a}    ${b}
    ${result}    Evaluate    ${a} + ${b}
    [Return]    ${result}

Log:

enter image description here

Ideally, I would like to get a log like this. Log contains arguments, returned results, all steps inside function and timestamps.

Upvotes: 2

Views: 10638

Answers (1)

Tomer Cohen
Tomer Cohen

Reputation: 332

I'm not sure I have a solution that will make your log look exactly like robot framework's log easily but I have a few tips for you:

  1. you don't have to supply the @allure.step decorator with any message, you can leave it empty and allure will take the name and arguments and print it for you.

  2. you can add allure log handler to your logger that way: in your conftest.py:

     def pytest_runtestloop(session) -> None:
         allure_logger = AllureLogger()
         if allure_logger not in logger.handlers:
             logger.info('Adding Allure logger')
             logger.addHandler(allure_logger)
    

    in another file allure_log_handler.py (or whatever you want):

     class AllureLogger(logging.Handler):
         def emit(self, record):
             if logging.DEBUG < record.levelno:  # print to allure only "info" messages
                 with allure.step(f'LOG ({record.levelname}): {record.getMessage()}'):
                     pass  # No need for content, since the step context is doing the work.
    

    now all "info" logs will be printed to allure and can clean up your code a little bit.

  3. you can wrap allure.step with your own decorator and use timeit or some other timing library to check how much time a method execution took and print it to your log.

  4. as long as you print to a logger you don't need the allure.attach(str(result), 'Result', allure.attachment_type.TEXT). allure can attach it automatically for your - here's how: How to append logs of Pytest into Allure Report

Upvotes: 3

Related Questions