0

I want to write a custom logger for a set of tests without making any significant changes to the tests. I would like to use something like a fixture that I can just pass to the test method and the fixture runs in the background during the entire duration of the test capturing stdout and stderr and changing it to custom messages. How can this be done ?

def test_1():
   blah
   blah
   print('Some msg')
   blah
   assert something, assert_msg

Output:
Some msg (if assert fails, then assertion error too)

What I want is

@fixture
def logger():
    capture stdout, stderr
    custom_msg = cust_msg(stdout, stderr)
    print(custom_msg)


def test_1(logger):
   blah
   blah
   print('Some msg')
   blah
   assert something, assert_msg

Output:
Custom msg (if assert fails, then custom assertion error too)
S. S.
  • 1
  • 1

1 Answers1

0

You can't capture the stdout from within the module without redefining either sys.stdout or print. Redefining print is easier, as it is a function already. And to catch the failed asserts, just catch AssertionErrors.

import functools
import sys

def fixture(f):
    old_print = print
    @functools.wraps(f)
    def wrapped(*args, **kwargs):
        global print
        def print(*value, sep=' ', end='\n', file=sys.stdout, flush=False):
            msg = sep.join(map(str, value)) + end

            # Manipulate msg here

            file.write(manipulated)
            if flush:
                file.flush()
        try:
            return f(*args, **kwargs)
        except AssertionError as e:

            # Custom message on failed assertion
            msg = e.args[0] if len(e.args) == 1 else None
            old_print(manipulated)

        finally:
            print = old_print
    return wrapped

But, asserts don't really give much helpful information. I would use an actual test library, like unittest.

An alternative would be to run it as a subprocess.

Artyer
  • 31,034
  • 3
  • 47
  • 75