我工作的一个Python命令行接口程序,我觉得很无聊做testings时,例如,这里是该程序的帮助信息:
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
当我做了节目中的变化,并要测试的东西,我必须打开一个终端,输入命令(含选项和参数),输入型,并查看是否在运行中出现任何错误。 如果真的发生错误,我必须回去给编辑从顶部检查代码结束,猜测其中的错误立场,使小的变化,写print
线路,再返回到终端,运行命令...
递归。
所以我的问题是,什么是做CLI程序测试的最佳方法,可以把它像正常的Python脚本单元测试一样容易?
我认为这是完美的罚款对整个程序级功能测试。 它仍然可能测试每测试一个方面/选项。 这种方式可以确保该程序确实有效的整体。 编写单元测试通常意味着你执行你的测试更快和故障通常是容易解释/理解。 但是,单元测试通常更加依赖于程序结构,需要更多的重构工作,当你在内部改变的事情。
总之,使用py.test ,这里是测试LATIN1到UTF8转换为pyconv一个小例子::
# 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
安装pytest后(“PIP安装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 。 如果你忘记了片刻的细节,你可以从“运行”和“TMPDIR”提供了帮助您准备和运行测试的事实工作。 如果你要玩,你可以尝试插入一个失败断言语句或简单地“断言0”,然后看追溯或问题“py.test --pdb”进入一个python提示。
简短的回答是肯定的,你可以使用单元测试,而且应该。 如果你的代码结构良好,它应该是很容易的单独测试每一个组成部分,如果你需要到随时可以模拟sys.argv
,模拟使用不同的参数运行它。
所以我的问题是,什么是做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()
现在有没有什么区别,你将如何使用它:作为一个脚本或模块。 所以里面的单元测试代码,你可以直接导入foo
函数,调用它,让你想要的任何断言。
我不会测试程序作为一个整体,这不是一个很好的测试策略和实际上可能没有捕获错误的实际位置。 CLI界面只是前端到的API。 你可以通过你的单元测试,测试API,然后当您更改到一个特定的部分,你有一个测试的情况下行使这一变化。
因此,调整你的应用程序,以便测试API,而不是应用它的自我。 但是,你可以有一个实际上并运行完整的应用程序,并检查输出是正确的功能测试。
总之,是测试代码是一样的测试任何其他代码,但你必须测试的各个部分,而不是他们的整体组合,以确保您的更改不会破坏一切。
这不是Python的具体,但我做什么,测试命令行脚本是用各种预定输入和选择运行它们和正确的输出存储在一个文件中。 然后,对其进行测试,当我做出改变,我只需运行新的脚本,并管道输出到diff correct_output -
如果文件是相同的,它什么也不输出。 如果他们是不同的,它表明你在哪里。 这,如果你是在Linux或OS X只会工作; 在Windows上,你一定要找MSYS。
例:
python mycliprogram --someoption "some input" | diff correct_output -
为了使它更容易,你可以将所有这些测试运行添加到您的“使测试”的Makefile的目标,我以为你已经离开。 ;)
如果你同时运行许多这样的,你可以在那里每一个加入了失败的标签结束使得它一点更加明显:
python mycliprogram --someoption "some input" | diff correct_output - || tput setaf 1 && echo "FAILED"
您可以使用标准的unittest模块:
# python -m unittest <test module>
或用鼻子作为一个测试框架。 只要写在单独的目录,然后运行单元测试的经典文件:
# nosetests <test modules directory>
写单元测试是很容易。 只要遵循了单元测试在线手册