How to assert output with nosetest/unittest in python?

后端 未结 12 1217
野趣味
野趣味 2020-11-28 02:56

I\'m writing tests for a function like next one:

def foo():
    print \'hello world!\'

So when I want to test this function the code will b

相关标签:
12条回答
  • 2020-11-28 03:22

    I use this context manager to capture output. It ultimately uses the same technique as some of the other answers by temporarily replacing sys.stdout. I prefer the context manager because it wraps all the bookkeeping into a single function, so I don't have to re-write any try-finally code, and I don't have to write setup and teardown functions just for this.

    import sys
    from contextlib import contextmanager
    from StringIO import StringIO
    
    @contextmanager
    def captured_output():
        new_out, new_err = StringIO(), StringIO()
        old_out, old_err = sys.stdout, sys.stderr
        try:
            sys.stdout, sys.stderr = new_out, new_err
            yield sys.stdout, sys.stderr
        finally:
            sys.stdout, sys.stderr = old_out, old_err
    

    Use it like this:

    with captured_output() as (out, err):
        foo()
    # This can go inside or outside the `with` block
    output = out.getvalue().strip()
    self.assertEqual(output, 'hello world!')
    

    Furthermore, since the original output state is restored upon exiting the with block, we can set up a second capture block in the same function as the first one, which isn't possible using setup and teardown functions, and gets wordy when writing try-finally blocks manually. That ability came in handy when the goal of a test was to compare the results of two functions relative to each other rather than to some precomputed value.

    0 讨论(0)
  • 2020-11-28 03:26

    Or consider using pytest, it has built-in support for asserting stdout and stderr. See docs

    def test_myoutput(capsys): # or use "capfd" for fd-level
        print("hello")
        captured = capsys.readouterr()
        assert captured.out == "hello\n"
        print("next")
        captured = capsys.readouterr()
        assert captured.out == "next\n"
    
    0 讨论(0)
  • 2020-11-28 03:28

    Based on Rob Kennedy's answer, I wrote a class-based version of the context manager to buffer the output.

    Usage is like:

    with OutputBuffer() as bf:
        print('hello world')
    assert bf.out == 'hello world\n'
    

    Here's the implementation:

    from io import StringIO
    import sys
    
    
    class OutputBuffer(object):
    
        def __init__(self):
            self.stdout = StringIO()
            self.stderr = StringIO()
    
        def __enter__(self):
            self.original_stdout, self.original_stderr = sys.stdout, sys.stderr
            sys.stdout, sys.stderr = self.stdout, self.stderr
            return self
    
        def __exit__(self, exception_type, exception, traceback):
            sys.stdout, sys.stderr = self.original_stdout, self.original_stderr
    
        @property
        def out(self):
            return self.stdout.getvalue()
    
        @property
        def err(self):
            return self.stderr.getvalue()
    
    0 讨论(0)
  • 2020-11-28 03:30

    In python 3.5 you can use contextlib.redirect_stdout() and StringIO(). Here's the modification to your code

    import contextlib
    from io import StringIO
    from foomodule import foo
    
    def test_foo():
        temp_stdout = StringIO()
        with contextlib.redirect_stdout(temp_stdout):
            foo()
        output = temp_stdout.getvalue().strip()
        assert output == 'hello world!'
    
    0 讨论(0)
  • 2020-11-28 03:32

    Since version 2.7, you do not need anymore to reassign sys.stdout, this is provided through buffer flag. Moreover, it is the default behavior of nosetest.

    Here is a sample failing in non buffered context:

    import sys
    import unittest
    
    def foo():
        print 'hello world!'
    
    class Case(unittest.TestCase):
        def test_foo(self):
            foo()
            if not hasattr(sys.stdout, "getvalue"):
                self.fail("need to run in buffered mode")
            output = sys.stdout.getvalue().strip() # because stdout is an StringIO instance
            self.assertEquals(output,'hello world!')
    

    You can set buffer through unit2 command line flag -b, --buffer or in unittest.main options. The opposite is achieved through nosetest flag --nocapture.

    if __name__=="__main__":   
        assert not hasattr(sys.stdout, "getvalue")
        unittest.main(module=__name__, buffer=True, exit=False)
        #.
        #----------------------------------------------------------------------
        #Ran 1 test in 0.000s
        #
        #OK
        assert not hasattr(sys.stdout, "getvalue")
    
        unittest.main(module=__name__, buffer=False)
        #hello world!
        #F
        #======================================================================
        #FAIL: test_foo (__main__.Case)
        #----------------------------------------------------------------------
        #Traceback (most recent call last):
        #  File "test_stdout.py", line 15, in test_foo
        #    self.fail("need to run in buffered mode")
        #AssertionError: need to run in buffered mode
        #
        #----------------------------------------------------------------------
        #Ran 1 test in 0.002s
        #
        #FAILED (failures=1)
    
    0 讨论(0)
  • 2020-11-28 03:32

    I'm only just learning Python and found myself struggling with a similar problem to the one above with unit tests for methods with output. My passing unit test for foo module above has ended up looking like this:

    import sys
    import unittest
    from foo import foo
    from StringIO import StringIO
    
    class FooTest (unittest.TestCase):
        def setUp(self):
            self.held, sys.stdout = sys.stdout, StringIO()
    
        def test_foo(self):
            foo()
            self.assertEqual(sys.stdout.getvalue(),'hello world!\n')
    
    0 讨论(0)
提交回复
热议问题