python模块介绍- doctest解释器交互式测试

python模块介绍- doctest解释器交互式测试

#2012-05-21

#磁针石

 

#承接软件自动化实施与培训 验证码破解 软件破解 脚本开发 测试和python培训等

#gtalk:ouyangchongwu#gmail.com qq 37391319 博客:http://blog.csdn.net/oychw

#版权所有,转载刊登请来函联系

#自动化测试和python群组:http://groups.google.com/group/automation_testing_python

#武冈qq群:126668220都梁深圳湖南户外超级群:49494279


官方文档参考地址:http://docs.python.org/library/pydoc.html

官方实例:Lib/test/test_doctest.py.

doctest查找和执行交互式Python会话,检验其符合预期,常用场景如下:

  • 检验模块的文档字符串是最新的。
  • 衰退测试。
  • 书写教程。

Doctest比unittest简单,没有API。不过doctest没有fixture,不适合复杂的场景。

简单的示例:

defmy_function(a, b):

"""

>>>my_function(2, 3)

6

>>>my_function(’a’, 3)

’aaa’

"""

returna * b

执行结果:

>python-m doctest -v doctest_simple.py

Trying:

    my_function(2, 3)

Expecting:

    6

ok

Trying:

    my_function('a', 3)

Expecting:

    'aaa'

ok

1 itemshad no tests:

    doctest_simple

1 itemspassed all tests:

   2 tests in doctest_simple.my_function

2 testsin 2 items.

2 passedand 0 failed.

Testpassed.

-v表示显示详细执行过程。测试用例以空行或者下个解释器提示符表示结束。

使用ELLIPSIS选项,可以使用…匹配任何字符串,甚至可以跨行。比如:

classMyClass(object):

    pass

 

defunpredictable(obj):

    """Returns a new list containingobj.

 

    >>> unpredictable(MyClass())#doctest: +ELLIPSIS

    []

    """

    return [obj]

执行结果:

>python-m doctest -v doctest_ellipsis.py

Trying:

    unpredictable(MyClass()) #doctest:+ELLIPSIS

Expecting:

    []

ok

2 itemshad no tests:

    doctest_ellipsis

    doctest_ellipsis.MyClass

1 itemspassed all tests:

   1 tests in doctest_ellipsis.unpredictable

1 testsin 3 items.

1 passedand 0 failed.

Testpassed.

 对于字典等数据结构,key的顺序可能发生变化,最好是比较固定部分或者使用相等来比较。

defgroup_by_length(words):

    """Returns a dictionarygrouping words into sets by length.

 

    >>> grouped = group_by_length(['python', 'module', 'of', 'the', 'week' ])

    >>> grouped == { 2:set(['of']),

    ...              3:set(['the']),

    ...              4:set(['week']),

    ...              6:set(['python', 'module']),

    ...              }

    True

 

    """

    d = {}

    for word in words:

        s = d.setdefault(len(word), set())

        s.add(word)

return d

执行结果:

Trying:

    grouped = group_by_length([ 'python','module', 'of', 'the', 'week' ])

Expectingnothing

ok

Trying:

    grouped == { 2:set(['of']),

                 3:set(['the']),

                 4:set(['week']),

                 6:set(['python', 'module']),

                 }

Expecting:

    True

ok

1 itemshad no tests:

    doctest_hashed_values_tests

1 itemspassed all tests:

   2 tests indoctest_hashed_values_tests.group_by_length

2 testsin 2 items.

2 passedand 0 failed.

Testpassed.

 

   上面的group_by_length是记录单词长度和对应的单词。s = d.setdefault(len(word), set())部分,取出单词列表的集合,第一次出现这个长度的单词,就设置一个空集合。然后用s.add(word)为集合添加元素,因为集合是可变对象,对集合的修改同时也会反映在字典中。

Traceback会报出模块所在路径,给识别带来一定的难度。Doctest对Traceback有特殊处理,忽略了Traceback的body,重点放在Traceback的开头和例外类型以及信息上。另外还有可以忽略例外详细信息的IGNORE_EXCEPTION_DETAIL

defthis_raises():

    """This function alwaysraises an exception.

 

    >>> this_raises()

    Traceback (most recent call last):

    RuntimeError: here is the error

    """

    raise RuntimeError('here is the error')

执行结果:

>python-m doctest -v doctest_tracebacks_no_body.py

Trying:

    this_raises()

Expecting:

    Traceback (most recent call last):

    RuntimeError: here is the error

ok

1 itemshad no tests:

    doctest_tracebacks_no_body

1 itemspassed all tests:

   1 tests in doctest_tracebacks_no_body.this_raises

1 testsin 2 items.

1 passedand 0 failed.

Testpassed.

 

空行可以使用表示,#doctest: +REPORT_NDIFF可以显示详细的比较结果;+NORMALIZE_WHITESPACE用于去掉首尾的空格以及多余的换行。

模块、类、方法、函数都可以包含文档测试,还可以放置在字典__test__中,比如:

import doctest_private_tests_external

 

__test__= {

    'numbers':"""

>>>my_function(2, 3)

6

 

>>>my_function(2.0, 3)

6.0

""",

 

    'strings':"""

>>>my_function('a', 3)

'aaa'

 

>>>my_function(3, 'a')

'aaa'

""",

 

    'external':doctest_private_tests_external,

   

    }

 

defmy_function(a, b):

    """Returns a * b

    """

return a* b

执行结果:

>python-m doctest -v doctest_private_tests.py

Trying:

    my_function(['A', 'B', 'C'], 2)

Expecting:

    ['A', 'B', 'C', 'A', 'B', 'C']

ok

Trying:

    my_function(2, 3)

Expecting:

    6

ok

Trying:

    my_function(2.0, 3)

Expecting:

    6.0

ok

Trying:

    my_function('a', 3)

Expecting:

    'aaa'

ok

Trying:

    my_function(3, 'a')

Expecting:

    'aaa'

ok

2 itemshad no tests:

    doctest_private_tests

    doctest_private_tests.my_function

3 itemspassed all tests:

   1 tests indoctest_private_tests.__test__.external

   2 tests indoctest_private_tests.__test__.numbers

   2 tests indoctest_private_tests.__test__.strings

5 testsin 5 items.

5 passedand 0 failed.

Testpassed.

 

上面例子中doctest_private_tests_external文件如下,其中的doctest也被执行。不过导入对象的doctest不会执行。

#!/usr/bin/envpython

#encoding: utf-8

#

#Copyright (c) 2010 Doug Hellmann. All rights reserved.

#

"""Externaltests associated with doctest_private_tests.py.

>>>my_function([’A’, ’B’, ’C’], 2)

[’A’,’B’, ’C’, ’A’, ’B’, ’C’]

"""

Doctest还支持reStructuredText文件格式:

doctest_in_help.py的内容如下:

defmy_function(a, b):

    """

    >>> my_function(2, 3) #doctest:+NORMALIZE_WHITESPACE

    6

    >>> my_function('a', 3)

    'aaa'

    """

return a* b

 

doctest_in_help.rst的内容如下

 

===============================

How toUse doctest_in_help.py

===============================

 

Thislibrary is very simple, since it only has one function called

‘‘my_function()‘‘.

 

Numbers

=======

 

‘‘my_function()‘‘returns the product of its arguments. For numbers,

thatvalue is equivalent to using the ‘‘*‘‘ operator.

 

::

 

>>>from doctest_in_help import my_function

>>>my_function(2, 3)

6

 

It alsoworks with floating-point values.

 

::

 

>>>my_function(2.0, 3)

6.0

 

Non-Numbers

===========

 

Because‘‘*‘‘ is also defined on data types other than numbers,

‘‘my_function()‘‘works just as well if one of the arguments is a

string,a list, or a tuple.

 

::

 

>>>my_function('a', 3)

'aaa'

 

>>>my_function(['A', 'B', 'C'], 2)

['A','B', 'C', 'A', 'B', 'C']

 

执行结果:

>python-m doctest -v doctest_in_help.rst

Trying:

    from doctest_in_help import my_function

Expectingnothing

ok

Trying:

    my_function(2, 3)

Expecting:

    6

ok

Trying:

    my_function(2.0, 3)

Expecting:

    6.0

ok

Trying:

    my_function('a', 3)

Expecting:

    'aaa'

ok

Trying:

    my_function(['A', 'B', 'C'], 2)

Expecting:

    ['A', 'B', 'C', 'A', 'B', 'C']

ok

1 itemspassed all tests:

   5 tests in doctest_in_help.rst

5 testsin 1 items.

5 passedand 0 failed.

Testpassed.

 

 

注意以上方式需要导入被测模块。另外多个选项可以写在多行:

>>>print range(20) # doctest: +ELLIPSIS

...                 # doctest:+NORMALIZE_WHITESPACE

[0,    1, ...,  18,    19]

其他选项有:

doctest.DONT_ACCEPT_TRUE_FOR_1

doctest.DONT_ACCEPT_BLANKLINE

doctest.NORMALIZE_WHITESPACE

doctest.IGNORE_EXCEPTION_DETAIL

doctest.SKIP

doctest.COMPARISON_FLAGS

doctest.REPORT_UDIFF

doctest.REPORT_UDIFF

doctest.REPORT_NDIFF

doctest.REPORT_ONLY_FIRST_FAILURE

doctest.REPORTING_FLAGS

通过doctest.register_optionflag(name),还可以自定义选项。

可以基于模块进行测试,比如当前模块doctest.testmod(),指定模块doctest.testmod(doctest_simple)。也可以基于文件测试doctest.testfile(’doctest_in_help.rst’)。推荐的使用方式如下:

if__name__ == "__main__":

    import doctest

    doctest.testmod()

testmod,testfile等函数中,可以指定verbose的模式,这样命令行的模式就会失效。模块的方式运行,比如python -mdoctest -v example.py,如果从其他模块包中导入了子模块,可能无法正确执行。

Testfile的定义如下:

doctest.testfile(filename[,module_relative][, name][, package][, globs][, verbose][, report][, optionflags][,extraglobs][, raise_on_error][, parser][, encoding])

返回值(failure_count, test_count)。

类似的函数有doctest.run_docstring_examples,doctest.testmod。

Unittest也可以集成进doctest。比如:

importdoctest

importunittest

 

importdoctest_simple

 

suite =unittest.TestSuite()

suite.addTest(doctest.DocTestSuite(doctest_simple))

suite.addTest(doctest.DocFileSuite('doctest_in_help.rst'))

 

runner =unittest.TextTestRunner(verbosity=2)

runner.run(suite)

执行结果:

>pythondoctest_unittest.py

my_function(doctest_simple)

Doctest:doctest_simple.my_function ... ok

doctest_in_help.rst

Doctest:doctest_in_help.rst ... ok

 

----------------------------------------------------------------------

Ran 2tests in 0.032s

单元测试的API如下:

doctest.DocFileSuite(*paths[,module_relative][, package][, setUp][, tearDown][, globs][, optionflags][, parser][,encoding])

类似的有doctest.DocTestSuite,另外还有设置标识的doctest.DocTestSuite。

类、函数、方法、模块等的doctest测试都复制了模块的全局空间,他们互相独立,不过可以通过模块的可变变量互相交互。另外testmod和testfile也可以传入全局变量。

另外高级API有ExampleDocTest类。另外还有函数DocTestFinder,DocTestParser,DocTestRunner,OutputChecker等。

                            list of:

+------+                   +---------+

|module|--DocTestFinder-> | DocTest | --DocTestRunner-> results

+------+    |       ^     +---------+     |      ^    (printed)

            |        |    | Example |     |       |

            v        |    |   ...   |    v       |

           DocTestParser   | Example |  OutputChecker

Debug可以使用pdb。DebugRunner的子类DocTestRunner在碰到第一个失败的例子时会产生一个异常。DocTestSuite()生成的unittest用例支持unittest.TestCase定义的debug方法。文档测试中也可以调用pdb.set_trace()

相关模块如下:

doctest (http://docs.python.org/library/doctest.html)The standard library documentation

for thismodule.

TheMighty Dictionary (http://blip.tv/file/3332763) Presentation by Brandon

Rhodesat PyCon 2010 about the internal operations of the dict.

difflib(page 61) Python’s sequence difference computation library, used to produce

thendiff output.

Sphinx(http://sphinx.pocoo.org/) As well as being the documentation processing

tool forPython’s standard library, Sphinx has been adopted by many third-party

projectsbecause it is easy to use and produces clean output in several digital and

printformats. Sphinx includes an extension for running doctests as it processes

documentationsource files, so the examples are always accurate.

nose(http://somethingaboutorange.com/mrl/projects/nose/) Third-party test runner

withdoctest support

py.test(http://codespeak.net/py/dist/test/) Third-party test runner with doctest

support.

Manuel(http://packages.python.org/manuel/) Third-party documentation-based test

runnerwith more advanced test-case extraction and integration with Sphinx.

你可能感兴趣的:(python)