Reo*_*orx 20 python testing unit-testing command-line-interface
我正在开发一个python命令行界面程序,我在测试时发现它很无聊,例如,这里是程序的帮助信息:
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
Run Code Online (Sandbox Code Playgroud)
当我对程序进行更改并想要测试某些内容时,我必须打开一个终端,输入命令(带有选项和参数),输入enter,然后查看运行时是否发生任何错误.如果确实发生了错误,我必须回到编辑器并从头到尾检查代码,猜测bug的位置,进行小的更改,写print行,返回终端,再次运行命令......
递归.
所以我的问题是,使用CLI程序进行测试的最佳方法是什么,它可以像使用普通python脚本进行单元测试一样简单吗?
hpk*_*k42 13
我认为在整个程序级别上进行功能测试是完全可以的.每次测试仍然可以测试一个方面/选项.通过这种方式,您可以确保程序真正起作用.编写单元测试通常意味着您可以更快地执行测试,并且通常更容易理解或理解故障.但是单元测试通常更多地与程序结构相关联,当您在内部更改内容时需要更多的重构工作.
无论如何,使用py.test,这是一个测试pyconv ::的latin1到utf8转换的小例子::
# content of test_pyconv.py
import pytest
# we reuse a bit of pytest's 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
Run Code Online (Sandbox Code Playgroud)
安装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 =========================
Run Code Online (Sandbox Code Playgroud)
该示例通过利用pytest的fixture机制重用了pytest自己测试的一些内部机制,请参阅http://pytest.org/latest/fixture.html.如果您暂时忘记了详细信息,可以通过"run"和"tmpdir"来帮助您准备和运行测试.如果你想玩,你可以尝试插入一个失败的assert语句或简单地"断言0",然后查看回溯或发出"py.test --pdb"进入python提示符.
所以我的问题是,使用 CLI 程序进行测试的最佳方法是什么,它可以像使用普通 python 脚本进行单元测试一样简单吗?
唯一的区别是,当您将 Python 模块作为脚本运行时,其__name__属性设置为'__main__'. 所以一般来说,如果您打算从命令行运行脚本,它应该具有以下形式:
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()
Run Code Online (Sandbox Code Playgroud)
现在,如何使用它没有区别:作为脚本或作为模块。因此,在单元测试代码中,您只需导入foo函数、调用它并做出您想要的任何断言。
也许太少太晚了,但你总是可以使用
import os.system
result = os.system(<'Insert your command with options here'>
assert(0 == result)
Run Code Online (Sandbox Code Playgroud)
这样,您可以像从命令行一样运行程序,并评估退出代码。
(学习pytest后更新)你也可以使用capsys。(来自运行 pytest --fixtures)
sys.stdoutcapsys 启用对和的写入的文本捕获sys.stderr。
The captured output is made available via ``capsys.readouterr()`` method
calls, which return a ``(out, err)`` namedtuple.
``out`` and ``err`` will be ``text`` objects.
Run Code Online (Sandbox Code Playgroud)
从用户界面开始进行功能测试,然后逐步进行单元测试。感觉很困难,尤其是当您使用argparse模块或click包时,它们控制了应用程序入口点。
该CLI-测试助手Python包有对您的CLI编写测试一种全面的方法的例子和辅助功能(上下文管理器)。这是一个简单的想法,并且与 TDD 完美配合:
注意:我假设您开发的代码与setup.py文件一起部署或作为模块运行 ( -m)。
这些测试很简单:它们运行您将在终端中输入的 shell 命令,例如
def test_entrypoint():
exit_status = os.system('foobar --help')
assert exit_status == 0
Run Code Online (Sandbox Code Playgroud)
请注意使用非破坏性操作(例如--help或--version)的技巧,因为我们无法使用此方法模拟任何内容。
为了测试单一方面的内部应用程序,你需要像命令行参数,也许环境变量模仿的东西。您还需要捕获脚本的退出,以避免测试因SystemExit异常而失败。
ArgvContext模拟命令行参数的示例:
@patch('foobar.command.baz')
def test_cli_command(mock_command):
"""Is the correct code called when invoked via the CLI?"""
with ArgvContext('foobar', 'baz'), pytest.raises(SystemExit):
foobar.cli.main()
assert mock_command.called
Run Code Online (Sandbox Code Playgroud)
请注意,我们模拟了我们希望 CLI 框架(click在此示例中)调用的函数,并且我们捕获SystemExit了框架自然引发的函数。上下文管理器由cli-test-helpers和pytest 提供。
剩下的一切照常营业。通过上述两种策略,我们克服了 CLI 框架可能剥夺了我们的控制权。剩下的就是通常的单元测试。希望采用 TDD 风格。
披露:我是cli-test-helpersPython 包的作者。