简体   繁体   中英

IndexError for sys.argv and testing command line args

I am writing a variety of unittest test cases along these lines:

import unittest
import program

class test(unittest.TestCase):
    a = 'acgt'
    b = 'c'
    sys.argv[1] = a
    sys.argv[2] = b
    text, patterns = program.parse()
    assertEqual(text, a)
    assertEqual(patterns, [b])

if __name__ == '__main__':
    unittest.main()

Unfortunately they all return an IndexError that the index is out of range. This makes sense, because nothing was passed on the command line. How can I prime sys.argv arguments in order to test methods which take them through a parser, from sys.argv?

What you likely got was not simply an index out of range but more specifically:

>>> import sys
>>> sys.argv[1] = 'foo'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list assignment index out of range

Unlike dict , a list isn't simply something you can assign arbitrary indexes with items, you have append an individual item or extend a list of items.

Also, your usage of unittest.TestCase is wrong. You have to define test methods and not just write your tests under a class declaration. It should look something like this:

class MyTestCase(unittest.TestCase):

    def test_program_parse(self):
        a = 'acgt'
        b = 'c'
        sys.argv.append(a)
        sys.argv.append(b)
        text, patterns = program.parse()
        assertEqual(text, a)
        assertEqual(patterns, [b])

Also, you most certainly don't want to dirty up sys.argv like so, you want to make use of setUp and tearDown to clean up the data, so you might have something like this within your test case class:

    def setUp(self):
        self._original_argv = sys.argv

    def tearDown(self):
        sys.argv = self._original_argv

By convention, these two methods should be the first two defined within your test class to make it clear and easy to see for future readers of your test cases.

Also, as you can see, you can just simply assign a new list to sys.argv such as the entire list of arguments you want to test against.

You can, by just changing the argv variable

class MyTest(unittest.TestCase):
    def test(self):
        import sys
        sys.argv = ['0', '1', '2']
        program()


def program():
    import sys
    print(sys.argv[1])
    print(sys.argv[2])

if __name__ == '__main__':
    unittest.main()

but this is not the recommended way. Rather change the signature of program so that it accepts a list so that you can easily control how it is called.

program(sys.argv)  # From command line
program(['param1', 'param2'])  # From Tests

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM