Тестирование модуля Python CLI
Я работаю над программой python Command-Line-Interface, и я нахожу это скучным при выполнении тестов, например, вот справочная информация о программе:
usage: pyconv [-h] [-f ENCODING] [-t ENCODING] [-o file_path] file_path
Convert text file from one encoding to another.
positional arguments:
file_path
optional arguments:
-h, --help show this help message and exit
-f ENCODING, --from ENCODING
Encoding of source file
-t ENCODING, --to ENCODING
Encoding you want
-o file_path, --output file_path
Output file path
Когда я внес изменения в программу и хочу что-то проверить, я должен открыть терминал,
введите команду (с параметрами и аргументами), введите enter и посмотрите, произошла ли какая-либо ошибка
во время бега. Если ошибка действительно возникает, я должен вернуться к редактору и проверить код
от начала до конца, угадывая, где места ошибок, делайте небольшие изменения, пишите строки print
вернитесь к терминалу, снова запустите команду...
Рекурсивный.
Итак, мой вопрос: какой лучший способ провести тестирование с помощью программы CLI, может ли быть так же легко
как модульное тестирование с обычными сценариями python?
Ответы
Ответ 1
Я считаю, что это прекрасно, чтобы функционально протестировать на уровне всей программы. По-прежнему можно проверить один аспект/опцию на тест. Таким образом, вы можете быть уверены, что программа действительно работает в целом. Написание единичных тестов обычно означает, что вы быстрее выполняете свои тесты и что ошибки обычно легче интерпретировать/понимать. Но модульные тесты, как правило, более привязаны к структуре программы, требуя больше усилий по рефакторингу, когда вы внутренне меняете вещи.
В любом случае, используя py.test, вот небольшой пример для тестирования преобразования latin1 в utf8 для pyconv::
# content of test_pyconv.py
import pytest
# we reuse a bit of pytest own testing machinery, this should eventually come
# from a separatedly installable pytest-cli plugin.
pytest_plugins = ["pytester"]
@pytest.fixture
def run(testdir):
def do_run(*args):
args = ["pyconv"] + list(args)
return testdir._run(*args)
return do_run
def test_pyconv_latin1_to_utf8(tmpdir, run):
input = tmpdir.join("example.txt")
content = unicode("\xc3\xa4\xc3\xb6", "latin1")
with input.open("wb") as f:
f.write(content.encode("latin1"))
output = tmpdir.join("example.txt.utf8")
result = run("-flatin1", "-tutf8", input, "-o", output)
assert result.ret == 0
with output.open("rb") as f:
newcontent = f.read()
assert content.encode("utf8") == newcontent
После установки pytest ( "pip install pytest" ) вы можете запустить его следующим образом:
$ py.test test_pyconv.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.4.5dev1
collected 1 items
test_pyconv.py .
========================= 1 passed in 0.40 seconds =========================
Пример повторного использования некоторых внутренних механизмов собственного тестирования pytest, используя механизм крепления pytest, см. http://pytest.org/latest/fixture.html. Если вы забыли о деталях на мгновение, вы можете просто работать из-за того, что для запуска и запуска тестов предусмотрены "run" и "tmpdir". Если вы хотите играть, вы можете попытаться вставить неудачный assert-оператор или просто "утвердить 0", а затем посмотреть трассировку или вывести "py.test -pdb", чтобы ввести приглашение python.
Ответ 2
Короткий ответ - да, вы можете использовать модульные тесты и должны. Если ваш код хорошо структурирован, достаточно проверить каждый компонент отдельно, и если вам нужно всегда моргнуть sys.argv
для имитации запуска его с разными аргументами.
Ответ 3
Итак, мой вопрос: какой лучший способ провести тестирование с помощью программы CLI, может ли быть так же просто, как и единичное тестирование с помощью обычных скриптов python?
Единственное различие заключается в том, что при запуске модуля Python в качестве script для его атрибута __name__
установлено значение '__main__'
. В общем, если вы намерены запустить команду script из командной строки, она должна иметь следующую форму:
import sys
# function and class definitions, etc.
# ...
def foo(arg):
pass
def main():
"""Entry point to the script"""
# Do parsing of command line arguments and other stuff here. And then
# make calls to whatever functions and classes that are defined in your
# module. For example:
foo(sys.argv[1])
if __name__ == '__main__':
main()
Теперь нет никакой разницы, как вы его используете: как script или как модуль. Таким образом, внутри кода тестирования устройства вы можете просто импортировать функцию foo
, вызвать его и сделать любые утверждения, которые вы хотите.
Ответ 4
Я бы не тестировал программу в целом, это не хорошая тестовая стратегия и, возможно, не поймает фактическое место ошибки. Интерфейс CLI - это просто интерфейс API. Вы тестируете API через свои модульные тесты, а затем, когда вы вносите изменения в определенную часть, у вас есть тестовый пример для осуществления этого изменения.
Итак, реструктурируйте приложение, чтобы вы тестировали API, а не приложение. Но у вас может быть функциональный тест, который фактически запускает полное приложение и проверяет правильность вывода.
Короче говоря, да, тестирование кода аналогично тестированию любого другого кода, но вы должны проверить отдельные части, а не их комбинацию в целом, чтобы гарантировать, что ваши изменения не сломают все.
Ответ 5
Это не для Python специально, но то, что я делаю для проверки сценариев командной строки, - это запустить их с различными предопределенными входами и параметрами и сохранить правильный вывод в файле. Затем, чтобы проверить их, когда я вношу изменения, я просто запускаю новый script и вывожу вывод в diff correct_output -
. Если файлы одинаковые, он ничего не выводит. Если они разные, это показывает, где. Это будет работать только в Linux или OS X; в Windows вам нужно будет получить MSYS.
Пример:
python mycliprogram --someoption "some input" | diff correct_output -
Чтобы сделать это еще проще, вы можете добавить все эти тестовые прогонки в свою целевую программу Make Make, которая, как я полагаю, у вас уже есть.;)
Если вы запускаете много из них сразу, вы можете сделать это немного более очевидным, когда каждый заканчивается добавлением тега fail:
python mycliprogram --someoption "some input" | diff correct_output - || tput setaf 1 && echo "FAILED"
Ответ 6
Может быть, слишком мало, слишком поздно,
но вы всегда можете использовать
import os.system
result = os.system(<'Insert your command with options here'>
assert(0 == result)
Таким образом, вы можете запустить вашу программу, как если бы она была из командной строки, и оценить код завершения.
Ответ 7
Вы можете использовать стандартный модуль unittest:
# python -m unittest <test module>
или используйте nose в качестве рамки тестирования. Просто напишите классические unittest файлы в отдельном каталоге и запустите:
# nosetests <test modules directory>
Написание unittests легко. Просто следуйте онлайн-руководству для unittesting