Search code examples
pythonunit-testingargparsestringio

Using unittest to check "--help" flag output


I have some code that parses command line options using argparse.

For example:

# mycode.py

import argparse

def parse_args():
    parser = argparse.ArgumentParser('my code')
    # list of arguments
    # ...
    # ...
    return vars(parser.parse_args())

if __name__ == "__main__":
    parse_args()

I would like to use unittest to check the output of the help function. I also don't want to change the actual code unless there is no other solution. The help action has a SystemExit call built into it after printing to stdout, so I have had to try and catch it in the unittest.

Here is my unittest code with the following steps:

1) Set the sys.argv list to include the -h flag.

2) Wrap the function call in a context manager to prevent the SystemExit being viewed as an error.

3) Switch the sys.stdout temporarily to an io.StringIO object so I can inspect it without having it print to screen.

4) Call the function in a try...finally block so the SystemExit isn't fatal.

5) Switch sys.stdout back to the real stdout.

6) Open a file to which I had previously saved the help text (by entering python mycode.py -h > help_out.txt in the terminal) to verify it is the same as the captured output from the StringIO.

import unittest
import mycode
import sys
import io

class TestParams(unittest.TestCase):

    def setUp(self):
        pass

    def test_help(self):
        args = ["-h"]
        sys.argv[1:] = args
        with self.assertRaises(SystemExit):
            captured_output = io.StringIO()
            sys.stdout = captured_output
            try:
                mycode.parse_args()
            finally:
                sys.stdout = sys.__stdout__
                with open("help_out.txt", "r") as f:
                    help_text = f.read()
                    self.assertEqual(captured_output, help_text)

    def tearDown(self):
        pass

This code works, but the captured_output StringIO object is empty, so the test fails.

I am looking for an explanation as to what is going wrong with the captured output and/or an alternative solution.


Solution

  • I was very close. The captured_output wasn't actually empty - I just wasn't accessing the contents correctly.

    Substitute captured_output.get_value() for captured_value in my example code and it works perfectly.