为什么导入模块会破坏我的doctest(Python 2.7)

时间:2016-12-09 23:43:38

标签: python python-2.7 doctest

我尝试在我的类中的StringIO中使用doctest实例,在Python 2.7程序中。我没有从测试中获得任何输出,而是得到了回复,而没有得到任何结果#34;。

这个简化的测试用例演示了错误:

#!/usr/bin/env python2.7
# encoding: utf-8

class Dummy(object):
    '''Dummy: demonstrates a doctest problem

    >>> from StringIO import StringIO
    ... s = StringIO()
    ... print("s is created")
    s is created
    '''

if __name__ == "__main__":
    import doctest
    doctest.testmod()

预期行为:测试通过。

观察到的行为:测试失败,输出如下:

% ./src/doctest_fail.py
**********************************************************************
File "./src/doctest_fail.py", line 7, in __main__.Dummy
Failed example:
    from StringIO import StringIO
    s = StringIO()
    print("s is created")
Expected:
    s is created
Got nothing
**********************************************************************
1 items had failures:
   1 of   1 in __main__.Dummy
***Test Failed*** 1 failures.

为什么这个doctest失败了?为了能够在我的doctests中使用类似StringIO的功能(带文件接口的文字字符串),我需要做些什么改变?

2 个答案:

答案 0 :(得分:5)

这是令人困惑的doctest解析器的延续行语法(...)。这有效:

#!/usr/bin/env python2.7
# encoding: utf-8

class Dummy(object):
    '''Dummy: demonstrates a doctest problem

    >>> from StringIO import StringIO
    >>> s = StringIO()
    >>> print("s is created")
    s is created
    '''

if __name__ == "__main__":
    import doctest
    doctest.testmod()

答案 1 :(得分:1)

[建立在wim的正确答案上,但通过查看基础doctest语义来解释为什么要多一点。]

示例失败,因为它在单独的简单语句前面使用PS2语法(...)而不是PS1语法(>>>)。

...更改为>>>

#!/usr/bin/env python2.7
# encoding: utf-8

class Dummy(object):
    '''Dummy: demonstrates a doctest problem

    >>> from StringIO import StringIO
    >>> s = StringIO()
    >>> print("s is created")
    s is created
    '''

if __name__ == "__main__":
    import doctest
    doctest.testmod()

现在,已更正的示例(重命名为doctest_pass.py)运行时没有错误。它不产生输出,这意味着所有测试都通过:

% src/doctest_pass.py                       

为什么>>>语法正确? doctest的Python库参考,25.2.3.2. How are Docstring Examples Recognized?应该是找到答案的地方,但对于这种语法并不十分清楚。

Doctest扫描文档字符串,查找“示例”。在它看到PS1字符串>>>的地方,它将从那里到行尾的所有内容作为示例。它还会将以PS2字符串...开头的任何后续行追加到示例中(请参阅类_EXAMPLE_RE中的doctest.DocTestParser,第584-595行)。它采用后续行,直到下一个以PS1字符串开头的空白行或行,作为通缉输出。

Doctest使用compile()中的exec statement内置函数将每个示例编译为Python“交互式语句”(参见:doctest.DocTestRunner.__run(),第1314-1315行)。

interactive statement”是以换行符或Compound Statement结尾的语句列表。复合语句,例如iftry语句,“一般来说,[......跨越]多行,尽管在简单的化身中,整个复合语句可能包含在一行中。”这是一个多行复合语句:

if 1 > 0:
    print("As expected")
else:
    print("Should not happen")

语句列表是一行中的一个或多个simple statement,以分号分隔。

from StringIO import StringIO
s = StringIO(); print("s is created")

因此,问题的doctest失败了,因为它包含一个带有三个简单语句的示例,并且没有分号分隔符。将PS2字符串更改为PS1字符串成功,因为它将docstring转换为三个示例的序列,每个示例都有一个简单的语句。虽然这三条线共同设置一个功能的一个测试,但它们不是一个单独的测试夹具。它们是三个测试,其中两个设置状态,但没有真正测试主要功能。

顺便说一下,您可以使用doctest标志查看-v识别的示例数。请注意,它说“3 tests in __main__.Dummy”。有人可能会认为这三行是一个测试单元,但是doctest看到了三个例子。前两个例子没有预期的输出。当Example执行并且不生成输出时,它将被视为“通过”。

% src/doctest_pass.py -v
Trying:
    from StringIO import StringIO
Expecting nothing
ok
Trying:
    s = StringIO()
Expecting nothing
ok
Trying:
    print("s is created")
Expecting:
    s is created
ok
1 items had no tests:
    __main__
1 items passed all tests:
   3 tests in __main__.Dummy
3 tests in 2 items.
3 passed and 0 failed.
Test passed.

在单个文档字符串中,示例按顺序执行。对于同一文档字符串中的以下示例,将保留每个示例的状态更改。因此import语句定义模块名称,s =赋值语句使用该模块名称并定义变量名称,依此类推。 doctest文档25.2.3.3. What’s the Execution Context?在它说“实例可以自由使用...之前定义的文档字符串中运行的名称时”倾斜地公开了这一点。

该部分的前一句话,“每次doctest找到要测试的文档字符串时,它都使用M的全局变量的浅层副本,这样...... M中的一个测试不能留下意外允许另一个测试工作的面包屑” ,有点误导。确实,M中的一个测试不会影响另一个文档字符串中的测试 。但是,在单个文档字符串中,较早的测试肯定会留下碎屑,这可能会影响以后的测试。

为什么doctest的Python库参考中的示例25.2.3.2. How are Docstring Examples Recognized?显示了...语法的示例?该示例显示了if语句,该语句是多行的复合语句。第二行和后续行标有PS2字符串。