How to test command line scripts with nose?
Question: I've created a Python library with some command-line scripts in a 'bin'
directory (so that `setup.py` will install it into 'bin' when installing it
with `pip`). Since this isn't a Python module, I can't work out how to test it
with nose.

How can I test a command line script that's part of a library using
`nose`/`unittest`?


Answer: Use the "[`if __name__ ==
"__main__":`](http://stackoverflow.com/questions/419163/what-does-if-name-
main-do)" idiom in your scripts and encapsulate _all of_ the _function_ -ality
in _function_ -s.

Then you can `import` your scripts into another script (such as a unit test
script) without the body of it being executed. This will allow you to write
unit-tests for the functionality and run them through `nose`.

I recommend keeping the "main" block to a line or two.

## For example:

### plus_one.py

    
    
    #!/usr/bin/env python
    
    import sys
    
    
    def main(args):
        try:
            output(plus_one(get_number(args)))
        except (IndexError, ValueError), e:
            print e
            return 1
        return 0
    
    
    def get_number(args):
        return int(args[1])
    
    
    def plus_one(number):
        return number + 1
    
    
    def output(some_text):
        print some_text
    
    
    if __name__ == '__main__':
        sys.exit(main(sys.argv))
    

You can test command-line parameters, output, exceptions and return codes in
your unittests...

### t_plus_one.py

    
    
    #!/usr/bin/env python
    
    from StringIO import StringIO
    import plus_one
    import unittest
    
    
    class TestPlusOne(unittest.TestCase):
    
        def test_main_returns_zero_on_success(self):
            self.assertEquals(plus_one.main(['test', '1']), 0)
    
        def test_main_returns_nonzero_on_error(self):
            self.assertNotEqual(plus_one.main(['test']), 0)
    
        def test_get_number_returns_second_list_element_as_integer(self):
            self.assertEquals(plus_one.get_number(['anything', 42]), 42)
    
        def test_get_number_raises_value_error_with_string(self):
            self.assertRaises(ValueError, plus_one.get_number, ['something',
                                                                'forty-two'])
    
        def test_get_number_raises_index_error_with_too_few_arguments(self):
            self.assertRaises(IndexError, plus_one.get_number, ['nothing'])
    
        def test_plus_one_adds_one_to_number(self):
            self.assertEquals(plus_one.plus_one(1), 2)
    
        def test_output_prints_input(self):
            saved_stdout, plus_one.sys.stdout = plus_one.sys.stdout, StringIO('_')
            plus_one.output('some_text')
            self.assertEquals(plus_one.sys.stdout.getvalue(), 'some_text\n')
            plus_one.sys.stdout = saved_stdout
    
    if __name__ == '__main__':
        unittest.main()
    

## Output

### `python plus_one.py 41`

    
    
    42
    

### `nosetests -v t_plus_one.py`

    
    
    test_get_number_raises_index_error_with_too_few_arguments (t_plus_one.TestPlusOne) ... ok
    test_get_number_raises_value_error_with_string (t_plus_one.TestPlusOne) ... ok
    test_get_number_returns_second_list_element_as_integer (t_plus_one.TestPlusOne) ... ok
    test_main_returns_nonzero_on_error (t_plus_one.TestPlusOne) ... ok
    test_main_returns_zero_on_success (t_plus_one.TestPlusOne) ... ok
    test_output_prints_input (t_plus_one.TestPlusOne) ... ok
    test_plus_one_adds_one_to_number (t_plus_one.TestPlusOne) ... ok
    
    ----------------------------------------------------------------------
    Ran 7 tests in 0.002s
    
    OK
    

