#python #unit-testing
Вопрос:
Я хочу протестировать сценарий, который выглядит следующим образом:
my_script.py
import argparse, sys
def my_parser(args=sys.argv[1:]):
parser = argparse.ArgumentParser(description="my parser")
parser.add_argument("--my-arg", dest="my_arg", required=True)
args, _ = parser.parse_known_args(args=args)
return args
args = my_parser()
def do_something():
res = 5 1
return res
def main():
use_args = args
res = do_something()
# whatevere here
Когда я пытаюсь запустить unittest для функции do_something()
, я всегда получаю ошибку синтаксического анализатора, требующего входных аргументов. Мой тест-это что-то вроде:
my_test.py
from unittest import TestCase
from my_script import sys, do_something
class MyTest(TestCase):
def test_do_something(self):
test_sys_argv = ["dummy", "--my-arg", "dummy_arg"]
with patch.object(sys, "argv", test_sys_argv):
out = do_something()
self.assertEqual(out, 6)
То, как я делаю патч, не работает. Я понимаю, что таким образом я исправляю любую ссылку sys.argv
на функцию do_something()
, в то время как при вызове теста sys.argv
для сценария она пуста.
Можно ли решить эту проблему без рефакторинга переменной args
внутри функции? Я хотел бы иметь args
переменную my_script.py в качестве глобальной переменной скрипта.
Заранее спасибо за любую помощь.
Ответ №1:
Если вы хотите протестировать my_parser
, просто введите его вручную в свой тест. если вы хотите протестировать do_something
, вам sys.argv
это вообще не нужно, так как fn никогда не использует args.
Проблема здесь как раз в том, что линия
args = my_parser()
Запускается всякий раз, когда сценарий импортируется, т. е. перед тестом.* Переместите его в main или в if __name__ == "__main__"
блок. Ничто не мешает args быть глобальным var, если вы действительно этого хотите:
args = None
if __name__ == "__main__":
args = my_parser()
Я сильно подозреваю, что вы будете оценивать аргументы только один раз-во время выполнения, и в этом случае я не совсем понимаю, почему вы просто не используете argparse
обычный способ без sys.argv.
Ваша попытка исправить sys.argv не работает, потому что это происходит после импорта, и функция уже получила пустой sys.argv, когда она была впервые определена. (Изменяемые аргументы по умолчанию привязываются, когда функция определена, хотя в данном случае это даже не имеет значения, так как функция вызывается до того, как вы приступите к тестированию.) Возможно, вы захотите определить его следующим образом:
def my_parser(args = None):
args = args or sys.argv[1:]
...
Поскольку sys.argv теперь вызывается внутри функции, она будет оцениваться во время выполнения, и вы можете исправить ее, если вам нужно. Помимо тестирования, я не вижу никаких причин для этого, так как вы обычно не изменяете sys.argv при запуске.
*Вы можете обойти это, импортировав внутри тестовой функции… но все дело в том, что в настоящее время ваша «глобальная переменная» в модуле небезопасна для импорта. Я не совсем уверен, каков здесь процесс выполнения.
P.S. Если вы пытаетесь протестировать приложение командной строки, возможно, вам захочется взглянуть на одну из платформ тестирования для моделирования вызовов с различными аргументами.
Комментарии:
1. Спасибо! Считаете ли вы, что использование
argparse
обычного способа решило бы проблему во время тестов?2. @Flexo использует argparse обычным способом, т. е. просто вызывает синтаксический анализатор без добавления sys.argv. вы просто сделаете свой код более обычным = проще для чтения другими. Проблема с тестами на данный момент заключается только в том, что вы пытаетесь проанализировать импорт , что, конечно, не удается.
3. Я вижу, большое спасибо