#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.
空行可以使用
模块、类、方法、函数都可以包含文档测试,还可以放置在字典__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有Example和DocTest类。另外还有函数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.