2013-08-10 2 views
79

У меня есть модуль Python, который использует библиотеку argparse. Как написать тесты для этого раздела базы кода?Как вы пишете тесты для части argparse модуля python?

+6

Я не уверен, как сказать это, не встречаясь, как придурок, но как мне привязать к чему-то, даже без слова «тест»? – pydanny

+0

argparse - это интерфейс командной строки. Напишите свои тесты для вызова приложения через командную строку. – Homer6

ответ

100

Вы должны реорганизовать свой код и переместить синтаксический к функции:

def parse_args(args): 
    parser = argparse.ArgumentParser(...) 
    parser.add_argument... 
    # ...Create your parser as you like... 
    return parser.parse_args(args) 

Затем в main функции вы должны просто назвать его:

parser = parse_args(sys.argv[1:]) 

(где первый элемент из sys.argv, который представляет имя сценария, удаляется, чтобы не отправлять его в качестве дополнительного переключателя во время работы CLI.)

В своих тестах, вы можете вызвать функцию синтаксического анализа с любым списком аргументов вы хотите, чтобы проверить его:

def test_parser(self): 
    parser = parse_args(['-l', '-m']) 
    self.assertTrue(parser.long) 
    # ...and so on. 

Таким образом, вы никогда не будете иметь, чтобы выполнить код вашего приложения только для проверки синтаксический анализатор.

Если вам необходимо изменить и/или добавить параметры к парсер позже в приложении, а затем создать фабричный метод:

def create_parser(): 
    parser = argparse.ArgumentParser(...) 
    parser.add_argument... 
    # ...Create your parser as you like... 
    return parser 

Вы можете позже манипулировать, если вы хотите, и тест может выглядеть следующим образом :

class ParserTest(unittest.TestCase): 
    def setUp(self): 
     self.parser = create_parser() 

    def test_something(self): 
     parsed = self.parser.parse_args(['--something', 'test']) 
     self.assertEqual(parsed.something, 'test') 
+2

Спасибо за ваш ответ. Как мы проверяем ошибки, когда определенный аргумент не передается? –

+2

@PratikKhadloya Если аргумент требуется и он не передан, argparse будет вызывать исключение. –

+0

Правильно, но я также пытаюсь проверить сообщение в исключении. Я не могу получить сообщение в объекте исключения. https://gist.github.com/tispratik/aebff28b8c5afd7bee59 –

4
  1. Заполняет список Арг с помощью sys.argv.append(), а затем вызвать parse(), проверьте результаты и повторите.
  2. Позвоните из файла batch/bash с вашими флагами и флаг args dump.
  3. Проведите анализ всех ваших аргументов в отдельном файле, а в сеансе разговора по телефону и дампе/оцените результаты, затем проверьте это из файла batch/bash.
3

простой способ тестирования анализатор является:

parser = ... 
parser.add_argument('-a',type=int) 
... 
argv = '-a 1 foo'.split() # or ['-a','1','foo'] 
args = parser.parse_args(argv) 
assert(args.a == 1) 
... 

Другой способ заключается в изменить sys.argv и вызвать args = parser.parse_args()

Есть много примеров тестирования argparse в lib/test/test_argparse.py

4

Сделайте свою main() функцию взять argv в качестве аргумента, а не позволяя ему read from sys.argv as it will by default:

# mymodule.py 
import argparse 
import sys 


def main(args): 
    parser = argparse.ArgumentParser() 
    parser.add_argument('-a') 
    process(**vars(parser.parse_args(args))) 
    return 0 


def process(a=None): 
    pass 

if __name__ == "__main__": 
    sys.exit(main(sys.argv[1:])) 

Затем вы можете проверить как обычно.

import mock 

from mymodule import main 


@mock.patch('mymodule.process') 
def test_main(process): 
    main([]) 
    process.assert_call_once_with(a=None) 


@mock.patch('foo.process') 
def test_main_a(process): 
    main(['-a', '1']) 
    process.assert_call_once_with(a='1') 
-1

Я обнаружил, что самый простой способ для меня, по крайней мере, было просто проверить sys.argv [0], чтобы увидеть, если питон пробежалась в python -m unittest и ничего не разобрать, если бы это было так.

import sys 
import argparse 

parser = argparse.ArgumentParser() 

parser.add_argument('--outdir', help='Directory to output to', \ 
    default='out') 
parser.add_argument('--file', help='Input file', \ 
    default='section') 
parser.add_argument('--word', help='Word to look up') 

if sys.argv[0] == 'python -m unittest': 
    args = parser.parse_args([]) 
else: 
    args = parser.parse_args() 
6

«argparse часть» немного расплывчатым, так что этот ответ сосредоточен на одной части: метод parse_args.Это метод, который взаимодействует с вашей командной строкой и получает все переданные значения. В принципе, вы можете издеваться над тем, что возвращает parse_args, так что ему не нужно фактически получать значения из командной строки.

import argparse 
import mock 


@mock.patch('argparse.ArgumentParser.parse_args', 
      return_value=argparse.Namespace(kwarg1=value, kwarg2=value)) 
def test_command(mock_args): 
    pass 

Вы должны включить все арг вашего метода командного в Namespace, даже если они не прошли. Дайте этим аргументам значение None. (см. docs) Этот стиль полезен для быстрого выполнения тестирования для случаев, когда для каждого аргумента метода передаются разные значения. Если вы решите издеваться над самим Namespace за то, что в ваших тестах не было уверенности в том, что он ведет себя аналогично действительному классу Namespace.

+0

Но теперь ваш код unittest также зависит от 'argparse' и его класса' Namespace'. Вы должны издеваться над «Пространством имен». –

+0

@DrunkenMaster извиняется за пристойный тон. Я обновил свой ответ с объяснением и возможным использованием. Я также учусь здесь, поэтому, если вы это сделаете, можете ли вы (или кто-то другой) предоставить случаи, когда издевательство над возвратным значением выгодно? (или, по крайней мере, случаи, когда * не * высмеивание возвращаемого значения является пагубным) –

Смежные вопросы