您的位置 首页 > 数码极客

【doct】Python 测试库 Doctest简介

介绍:今天无意中看到了《流畅的Python》本书,据说是有点难的书。

拿到之后就从后往前翻,还没翻到正文,仅仅翻到注意事项,就看到很多陌生的名词,有点伤自尊,真是有点吾生也有涯,而知也无涯的赶脚。但是也只能怀揣着幸好遇见您的心态,否则一辈子都不知自己的无知。

闲话少将,今天遇到第一个陌生词就是doctest。这个doctest是本书中程序做测试用的,而且本模块还是一个标准库。学习时也让我感到写测试用例是一名优秀程序员所具备的素质之一。



Doctest是python的一个标准库,主要用来做测试的。本文的学习资源主要依据官网文档:.

下面开始我们的学习。

doctest从名字中可以瞧出来它干什么,doc(文档)+ test(测试)。没错,这个库就是基于程序的的文档(docstring)做的。说到这里也许会恍然大悟,为啥之前读标准库或别人源码时,为啥把测试用例放到docstring中了,以为就是注释而已,其实还有测试的作用,可见doctest也是用心良苦呢。


1.使用样例

doctest主要支持两种测试方式,一种是基于代码的Docstrings,一种是基于测试文件,即将测试用例的代码放到一个文件中,这种方式是为了一些太复杂的测试写入代码,导致代码污染。上面两种方式划分仅仅是根据这个测试用例是放在哪。

(1) 基于Docstrings的测试

我们知道python函数或类是有docsting这个对象的,主要用来存放说明文档。这里的测试就是基于说明文档做的。直接上官方例子,文件名为exam:

""" This is the "example" module. The example module supplies one function, factorial(). For example, >>> factorial(5) 120 """ def factorial(n): """Return the factorial of n, an exact integer >= 0. >>> [factorial(n) for n in range(6)] [1, 1, 2, 6, 24, 120] >>> factorial(30) 265252859812191058636308480000000 >>> factorial(-1) Traceback (most recent call last): ... ValueError: n must be >= 0 Factorials of floats are OK, but the float must be an exact integer: >>> factorial) Traceback (most recent call last): ... ValueError: n must be exact integer >>> factorial) 265252859812191058636308480000000 It must also not be ridiculously large: >>> factorial(1e100) Traceback (most recent call last): ... OverflowError: n too large """ import math if not n >= 0: raise ValueError("n must be >= 0") if ma(n) != n: raise ValueError("n must be exact integer") if n+1 == n: # catch a value like 1e300 raise OverflowError("n too large") result = 1 factor = 2 while factor <= n: result *= factor factor += 1 Return result if __name__ == "__main__": import doctest doc()

代码中定义了 factorial函数,在定义factorial函数下面有一大串三个双引号(“”“)括起来的说明文字,这块内容就是docstrings。而里面除了函数的作用描述之外,还有测试用例的样例和放回结果。这就是doctest能起作用的重点。

在__name__函数中即使调用doc()函数对本模块进行的测试。本例子执行结果是什么也没有返回,为啥? 恭喜,你的程序在指定的测试用例内,执行结果和预期相符,没有任何错误出现。 如果有错误出现的话,就会返回你测试用例报告,包含信息有执行的数量,成果的个数和失败的个数等。

当然也可以在成功时打印测试报告,这样就将测试的调用函数doc中的参数verbose=True。因为默认情况下verbose=False

doc(verbose=True)

这时执行结果为:

Trying:     [factorial(n) for n in range(6)] Expecting:     [1, 1, 2, 6, 24, 120] ok Trying:     factorial(30) Expecting:     265252859812191058636308480000000 ok Trying:     factorial(-1) Expecting:     Traceback (most recent call last):         ...     ValueError: n must be >= 0 ok Trying:     factorial) Expecting:     Traceback (most recent call last):         ...     ValueError: n must be exact integer ok Trying:     factorial) Expecting:     265252859812191058636308480000000 ok Trying:     factorial(1e100) Expecting:     Traceback (most recent call last):         ...     OverflowError: n too large ok 1 items had no tests:     __main__ 1 items passed all tests:    6 tests in __main__.factorial 6 tests in 2 items. 6 passed and 0 failed. Test passed.

另外,如果使用交互式命令执行以上代码,也可以的。

$ python exam $

这种情况也是没有输出测试报告的,怎么办?添加一个参数, -v

$ python exam -v

这样就可以看到上面的测试报告了。

加入我们的model文件中没有__name__函数,也就是没有调用doctest模块的测试函数,在交互式命令模式也是可以实现相同效果的。

python -m doctest -v exam


(2) 基于文档文件的测试

代码中插入适量的测试用例是可以接受的,但如果测试用例太复杂,以至于放到docstrings不合适,但有需要使用doctest进行测试,怎么办? doc()解决这个问题。上面的示例的docstrings放入exam中:

The ``example`` module ====================== Using ``factorial`` ------------------- This is an example text file in reStructuredText format. First import ``factorial`` from the ``example`` module: >>> from example import factorial Now use it: >>> factorial(6) 120

注意:这里面多类 >>> from example import factorial,这个是导入测试代模块和函数的。

这是测试代码改成

import doctest doc("exam")

如果在交互式命令模式下执行且输出测试报告,使用下面的命令行。

python -m doctest -v exam


2. doctest工作机制

  1. docstring书写格式。

上面两种测试模式,都是基于docstring做的,只是存放位置不同而已,有点新瓶装旧酒的意味。可见关键还是这个“酒”如何。docstring格式有以下约束:

>>> # comments are ignored >>> x = 12 >>> x 12 >>> if x == 13: ... print("yes") ... else: ... print("no") ... print("NO") ... print("NO!!!") ... no NO NO!!! >>>


  • >>> ... 开头的行,表示执行的代码
  • 输出不能说空白行,因为空白行代表输出结束。如果真是表示输出空白行的话,使用<BLANKLINE>
  • 反斜线也是有益


3.doctest api

  1. doc函数


def testmod(m=None, name=None, globs=None, verbose=None, report=True, optionflags=0, extraglobs=None, raise_on_error=False, exclude_empty=False): """m=None, name=None, globs=None, verbose=None, report=True, optionflags=0, extraglobs=None, raise_on_error=False, exclude_empty=False Test examples in docstrings in functions and classes reachable from module m (or the current module if m is not supplied), starting with m.__doc__. Also test examples reachable from dict m.__test__ if it exists and is not None. m.__test__ maps names to functions, classes and strings; function and class docstrings are tested even if the name is private; strings are tested directly, as if they were docstrings. Return (#failures, #tests). See help(doctest) for an overview. Optional keyword arg "name" gives the name of the module; by default use m.__name__. Optional keyword arg "globs" gives a dict to be used as the globals when executing examples; by default, use m.__dict__. A copy of this dict is actually used for each docstring, so that each docstring's examples start with a clean slate. Optional keyword arg "extraglobs" gives a dictionary that should be merged into the globals that are used to execute examples. By default, no extra globals are used. This is new in 2.4. Optional keyword arg "verbose" prints lots of stuff if true, prints only failures if false; by default, it's true iff "-v" is in . Optional keyword arg "report" prints a summary at the end when true, else prints nothing at the end. In verbose mode, the summary is detailed, else very brief (in fact, empty if all tests passed). Optional keyword arg "optionflags" or's together module constants, and defaults to 0. This is new in 2.3. Possible values (see the docs for details): DONT_ACCEPT_TRUE_FOR_1 DONT_ACCEPT_BLANKLINE NORMALIZE_WHITESPACE ELLIPSIS SKIP IGNORE_EXCEPTION_DETAIL REPORT_UDIFF REPORT_CDIFF REPORT_NDIFF REPORT_ONLY_FIRST_FAILURE Optional keyword arg "raise_on_error" raises an exception on the first unexpected exception or failure. This allows failures to be post-mortem debugged. Advanced tomfoolery: testmod runs methods of a local instance of class doc, then merges the results into (or creates) global Tester instance doc. Methods of doc can be called directly too, if you want to do something unusual. Passing report=0 to testmod is especially useful then, to delay displaying a summary. Invoke doc.summarize(verbose) when you're done fiddling. """


2.doc 函数

def testfile(filename, module_relative=True, name=None, package=None, globs=None, verbose=None, report=True, optionflags=0, extraglobs=None, raise_on_error=False, parser=DocTestParser(), encoding=None): """ Test examples in the given file. Return (#failures, #tests). Optional keyword arg "module_relative" specifies how filenames should be interpreted: - If "module_relative" is True (the default), then "filename" specifies a module-relative path. By default, this path is relative to the calling module's directory; but if the "package" argument is specified, then it is relative to that package. To ensure os-independence, "filename" should use "/" characters to separate path segments, and should not be an absolute path ., it may not begin with "/"). - If "module_relative" is False, then "filename" specifies an os-specific path. The path may be absolute or relative (to the current working directory). Optional keyword arg "name" gives the name of the test; by default use the file's basename. Optional keyword argument "package" is a Python package or the name of a Python package whose directory should be used as the base directory for a module relative filename. If no package is specified, then the calling module's directory is used as the base directory for module relative filenames. It is an error to specify "package" if "module_relative" is False. Optional keyword arg "globs" gives a dict to be used as the globals when executing examples; by default, use {}. A copy of this dict is actually used for each docstring, so that each docstring's examples start with a clean slate. Optional keyword arg "extraglobs" gives a dictionary that should be merged into the globals that are used to execute examples. By default, no extra globals are used. Optional keyword arg "verbose" prints lots of stuff if true, prints only failures if false; by default, it's true iff "-v" is in . Optional keyword arg "report" prints a summary at the end when true, else prints nothing at the end. In verbose mode, the summary is detailed, else very brief (in fact, empty if all tests passed). Optional keyword arg "optionflags" or's together module constants, and defaults to 0. Possible values (see the docs for details): DONT_ACCEPT_TRUE_FOR_1 DONT_ACCEPT_BLANKLINE NORMALIZE_WHITESPACE ELLIPSIS SKIP IGNORE_EXCEPTION_DETAIL REPORT_UDIFF REPORT_CDIFF REPORT_NDIFF REPORT_ONLY_FIRST_FAILURE Optional keyword arg "raise_on_error" raises an exception on the first unexpected exception or failure. This allows failures to be post-mortem debugged. Optional keyword arg "parser" specifies a DocTestParser (or subclass) that should be used to extract tests from the files. Optional keyword arg "encoding" specifies an encoding that should be used to convert the file to unicode. Advanced tomfoolery: testmod runs methods of a local instance of class doc, then merges the results into (or creates) global Tester instance doc. Methods of doc can be called directly too, if you want to do something unusual. Passing report=0 to testmod is especially useful then, to delay displaying a summary. Invoke doc.summarize(verbose) when you're done fiddling. """


4.单元测试API

这方面同unitest相关性大,另做一篇介绍

5.高级API

对于一个文档解析流程需要 find, parse, run 和check excample 内容。对于流程中需要的对象分别由

DocTestFinder, DocParser, DoctestRunner和OutputChecher实现。

具体流程见下图:

list of: +------+ +---------+ |module| --DocTestFinder-> | DocTest | --DocTestRunner-> results +------+ | ^ +---------+ | ^ (printed) | | | Example | | | v | | ... | v | DocTestParser | Example | OutputChecker +---------+


4.应用场景

  1. j测试文档示例
  2. 回退测试
  3. 可执行的文档文字测试


关于作者: admin

无忧经验小编鲁达,内容侵删请Email至wohenlihai#qq.com(#改为@)

热门推荐