3

I have a python program of about 500 lines that writes to stdout (using print statements). Now I'd like to make some changes and do refactoring of the program, but I want to make sure that in the process of doing so I keep getting the same output (given the same input, of course).

What would be a good strategy to do so, without rewriting the functions to return strings (allowing for easier testing) instead of the current print-ing?

I though of redirecting the initial output (before I start changing it) to a text file. How can I then easily and automatically check the output of the modified program with the textfile (without redirecting that output again to a temporary text file and comparing the files)?

Edit: This is the solution I settled on:

def test_something():
    # using lambda because we might test function with parameters
    f = lambda: thing_to_test
    test_generic('expect_output.txt', f)

def test_generic(filename_expected, function_to_test):

    #Run and write to tmp file
    tmpfile = 'test-tmp.txt'
    sys.stdout = open(tmpfile, 'w')
    function_to_test()
    sys.stdout = sys.__stdout__

    #compare with expected output
    expected = open(filename_expected).read()
    result = open(tmpfile).read()
    d = difflib.Differ()
    diff = d.compare(expected.splitlines(), result.splitlines())

    #print result (different lines only)
    diff_lines_only = [line for line in diff if line[0] in "+-?"]
    if not diff_lines_only:
        print "Test succeeded (%s)\n" % filename_expected
        os.remove(tmpfile)
    else:
        print "Test FAILED (%s):\n" % filename_expected
        print '\n'.join(list(diff_lines)) 

Edit 2: Actually, I think the doctest solution which I provided as an answer below is much nicer.

4

4 回答 4

2

您可以获得代表程序重定向输出的字符串sys.stdout。要比较输出,您可以使用该difflib模块。特别是Differ该类或多或少地执行diff命令:

>>> import difflib
>>> text = '''bar
... baz
... '''
>>> text2 = '''foo
... bar
... '''
>>> d = difflib.Differ()
>>> for line in d.compare(text.splitlines(), text2.splitlines()):
...     print line
... 
+ foo
  bar
- baz

如果我没记错 unittest2assertEqual已经尝试显示字符串的差异,但我不知道在什么级别以及输出是否足够简单。

于 2012-11-08T20:12:27.797 回答
1

有趣的小示例问题 - 我用它创建了一个使用 pytest 的打印/捕获/差异比较解决方案。请注意,此示例对 pytest 功能进行了高级使用,但这些功能已链接并完全记录在案,并且在许多其他情况下也很有用。实际上,该解决方案所需的代码不到当前顶级解决方案的一半,您可能会发现能够有选择地运行测试或其他一些 pytest 功能非常好

于 2012-11-09T10:35:17.377 回答
1

使用doctest库的解决方案,我认为它实际上更好且独立,因为测试代码和预期的输出现在放在一个文件中。

在 python 脚本中(实际上,我将它包含在我的主程序中,如果我提供“test”作为第一个命令行参数,则执行):

import doctest
doctest.testfile('test-program.txt', optionflags = doctest.NORMALIZE_WHITESPACE)

测试文件test-program.txt现在遵循以下内容:

>>> import my_python_script
>>> whatever_I want_to_test_or_call_goes_here
and_here_I pasted_the_expected_output

这具有访问所有doctest功能的额外好处(例如-v用于更详细输出的开关)。因此,我只需从命令行执行以下操作即可获得完整报告:

 C:\wherever> python my_python_script test -v
于 2012-11-09T10:44:00.700 回答
1

假设你正在运行 Bash,你可以运行diff -u orig-file <(python my-program.py). 这将在原始文件(您已经将原始输出写入其中)和您的程序将写入的命名管道之间进行比较。

这是一个简单的简单示例,使用echo而不是实际的 Python 脚本:

$ diff -u <(echo $'foo\nbar\nbaz') <(echo $'foo\nbar\nquux')
--- /dev/fd/63  2012-11-08 15:07:09.000000000 -0500
+++ /dev/fd/62  2012-11-08 15:07:09.000000000 -0500
@@ -1,3 +1,3 @@
 foo
 bar
-baz
+quux
于 2012-11-08T20:05:16.147 回答