Python单元测试模块doctest的具体使用

1. 什么是doctest?

在Python中,我们经常需要编写测试代码,来确保程序的正确性。Python内置了一个名为doctest的测试模块,旨在测试Python模块的文档字符串。

doctest是Python的一个模块,它可以从文档字符串中提取测试用例,然后执行这些测试用例,从而验证代码是否正确。文档字符串是Python中的特殊注释,用三个引号括起来的一段字符串,通常放在函数定义的下面。在这个字符串中,我们可以写一些示例代码,doctest可以自动执行这些示例代码,并检验代码的输出是否与期望值一致。

2. doctest的基本用法

doctest最简单的用法就是在代码的文档字符串中添加一些示例代码。这些示例代码可以是函数的调用示例,也可以是模块的使用示例。例如,下面是一个简单的doctest示例,其中包含了一个函数和一个模块:

def average(numbers):

"""

Return the average of a sequence of numbers.

Examples:

>>> average([1, 2, 3, 4, 5])

3.0

>>> average([10, 20, 30, 40, 50])

30.0

"""

return sum(numbers) / len(numbers)

if __name__ == '__main__':

import doctest

doctest.testmod()

在这个示例中,我们定义了一个函数average,它计算一个数字列表的平均值。在函数的文档字符串中,我们使用了三个引号括起来的一段字符串,其中包含两个示例:average([1, 2, 3, 4, 5])应该返回3.0,average([10, 20, 30, 40, 50])应该返回30.0。然后,我们使用if __name__ == '__main__'语句来指定当脚本作为主程序运行时需要执行的代码,这里使用了doctest.testmod()函数来执行doctest测试。

3. doctest的常用选项

doctest还提供了许多选项来控制测试的行为。下面我们来介绍其中一些常用的选项。

3.1. VERBOSE

VERBOSE选项用于指定测试结果的输出方式,如果将VERBOSE设置为True,doctest会输出测试用例的详细信息,显示哪些测试用例通过了,哪些没有通过。例如,下面的脚本将会输出详细的测试结果:

import doctest

def add(x, y):

"""

>>> add(1,2)

3

>>> add('hello','world')

'helloworld'

"""

return x + y

if __name__ == '__main__':

doctest.testmod(verbose=True)

输出结果如下所示:

Trying:

add(1,2)

Expecting:

3

ok

Trying:

add('hello','world')

Expecting:

'helloworld'

ok

1 items had no tests:

__main__

1 items passed all tests:

2 tests in __main__.add

2 tests in 2 items.

2 passed and 0 failed.

Test passed.

输出的结果是经过详细解释的。我们可以看到,doctest首先执行了第一个示例,输入参数(1, 2),调用函数add(1, 2),得到的结果是3,测试通过了。然后,doctest继续执行第二个示例,输入参数('hello', 'world'),调用函数add('hello', 'world'),得到的结果是'helloworld',测试也通过了。

3.2. ELLIPSIS

ELLIPSIS选项用于在测试中忽略某些输出内容。如果将ELLIPSIS设置为True,doctest会将输出中的一些内容替换为省略号。这个选项通常用于测试函数生成的长字符串。例如,我们来看一个简单的例子:

def generate_hello_world_string():

"""

>>> generate_hello_world_string()

'Hello, world!'

>>> generate_hello_world_string() # doctest: +ELLIPSIS

'Hello, ...!'

"""

return 'Hello, world!'

在这个示例中,我们定义了一个函数generate_hello_world_string,它生成一个字符串'Hello, world!'。在示例中,我们使用了两个测试用例来测试这个函数。第一个测试用例要求函数生成字符串'Hello, world!',而第二个测试用例添加了doctest: +ELLIPSIS选项,表示我们要忽略'world'之后的内容。这个选项通常用于测试一些字符串,例如HTML代码、XML代码等。

3.3. NORMALIZE_WHITESPACE

NORMALIZE_WHITESPACE选项用于在测试中忽略一些空白字符,例如空格和制表符。如果将NORMALIZE_WHITESPACE设置为True,doctest会将输出中的空格和制表符替换为单个空格。这个选项通常用于测试生成的文本文件、HTML文件等。例如,我们来看一个简单的例子:

def generate_html():

"""

>>> generate_html()

'<html><body><p>Hello, world!</p></body></html>'

>>> generate_html() # doctest: +NORMALIZE_WHITESPACE

'<html> <body> <p> Hello, world! </p> </body> </html>'

"""

return '<html><body><p>Hello, world!</p></body></html>'

在这个示例中,我们定义了一个函数generate_html,它生成一个HTML文件。在示例中,我们使用了两个测试用例来测试这个函数。第一个测试用例要求函数生成一个带有HTML标记的字符串,而第二个测试用例添加了doctest: +NORMALIZE_WHITESPACE选项,表示我们要忽略字符串中的一些空白字符。

4. doctest的高级用法

除了基本用法和常用选项外,doctest还提供了一些高级用法,例如在测试中使用上下文管理器、使用subTest、使用SKIP条件、使用命令行参数等。

4.1. 使用上下文管理器

在函数或方法中使用上下文管理器是一种常见的编程模式。在Python中,使用with语句可以很方便地管理上下文。doctest也支持在测试中使用上下文管理器。例如,下面是一个示例,其中包含了一个具有上下文管理器的函数:

import doctest

def read_file(filename):

"""

>>> with open('test.txt', 'w') as f:

... f.write('hello\\nworld\\n')

>>> read_file('test.txt')

'hello\\nworld\\n'

"""

with open(filename) as f:

return f.read()

if __name__ == '__main__':

doctest.testmod()

在这个示例中,我们定义了一个函数read_file,它读取一个文件的内容,并返回这个内容。在函数的文档字符串中,我们使用with语句来创建一个文件,并将一些字符串写入文件中。然后,我们使用read_file函数来读取这个文件的内容,并将其与期望值进行比较。

4.2. 使用subTest

在测试中使用subTest可以方便地测试多个实例。subTest是Python3.4版本中新增的一个功能,它可以将多个测试放在一个测试用例中执行,并显示每个测试的名称和状态。例如,下面是一个示例,其中使用了subTest:

import doctest

def divide(x, y):

"""

>>> divide(10, 2)

5.0

>>> divide(10, 0)

Traceback (most recent call last):

...

ZeroDivisionError: division by zero

>>> divide(0, 10)

0.0

"""

try:

result = x / y

except ZeroDivisionError:

raise

else:

return result

if __name__ == '__main__':

with doctest.Example('*'):

for data in [(10, 2, 5.0), (10, 0, ZeroDivisionError), (0, 10, 0.0)]:

with doctest.subTest(*data):

d = divide(data[0], data[1])

if isinstance(data[2], type):

assert isinstance(d, data[2])

else:

assert d == data[2]

在这个示例中,我们定义了一个函数divide,它计算两个数之间的商。在函数的文档字符串中,我们使用了三个测试用例来测试这个函数。然后,我们使用with doctest.Example('*')来指定每个测试用例的名称,这里将测试用例的名称设置为'*'。这里使用with语句来管理测试用例的执行。

接下来,我们使用for循环来遍历多个数据,然后使用with doctest.subTest(*data)将每个数据包装为一个测试用例。最后,我们使用assert语句来判断测试是否通过。

4.3. 使用SKIP条件

在测试中,有时候需要跳过一些测试。doctest提供了SKIP条件来实现这个功能。SKIP条件用于指定一些条件,如果这些条件满足,就跳过测试。例如,下面是一个示例,其中使用了SKIP条件:

import sys

import doctest

def add(x, y):

"""

>>> add(1, 2)

3

>>> add(3, 4)

7

>>> add(5, 6)

Traceback (most recent call last):

...

ValueError: value out of range

"""

if sys.version_info.major == 2:

raise ValueError('value out of range')

return x + y

if __name__ == '__main__':

doctest.testmod(optionflags=doctest.SKIP)

在这个示例中,我们定义了一个函数add,它计算两个数的和。在函数的文档字符串中,我们使用了三个测试用例来测试这个函数。然后,我们使用了doctest.SKIP选项来跳过测试。

4.4. 使用命令行参数

在运行doctest时,可以指定一些命令行参数来控制测试的行为。例如,可以使用-v选项来显示详细的测试结果。例如,下面是一个示例,其中使用了命令行参数:

import doctest

def add(x, y):

"""

This function adds two numbers.

Examples:

>>> add(1, 2)

3

>>> add(3, 4)

7

"""

return x + y

if __name__ == '__main__':

import sys

args = [sys.argv[0], '-v']

doctest.testmod(argv=args)

在这个示例中,我们定义了一个函数add,它计算两个数的和。在函数的文档字符串中,我们使用了两个测试用例来测试这个函数。然后,我们使用sys.argv指定命令行参数,将-v选项传递给testmod函数。

5. 总结

本文介绍了Python中的doctest测试模块的具体用法,包括doctest的基本用法、常用选项以及高级用法。doctest是Python内置的测试模块,它可以从文档字符串中提取测试用例进行测试,非常方便。在编写Python程序时,建议将测试代码与程序代码一同编写,以确保程序的正确性。

免责声明:本文来自互联网,本站所有信息(包括但不限于文字、视频、音频、数据及图表),不保证该信息的准确性、真实性、完整性、有效性、及时性、原创性等,版权归属于原作者,如无意侵犯媒体或个人知识产权,请来电或致函告之,本站将在第一时间处理。猿码集站发布此文目的在于促进信息交流,此文观点与本站立场无关,不承担任何责任。

后端开发标签