Unittest 和 Doctest 的区别

Unittest 和 Doctest 的区别

本教程将介绍 Python 中测试代码的两个主要框架:Doctest 和 Unittest,并讨论它们之间的区别。测试是软件开发的关键阶段,确保代码质量、可重用性和敏捷性。通过使用各种测试用例对代码库进行测试,可以避免代码破坏并最小化漏洞暴露。Python 提供了两个主要的测试框架:Doctest 和 Unittest,下面我们将探讨这两个框架。

Doctest 简介

Doctest 模块是内置的,这意味着它随 Python 安装而来,无需单独安装。Doctest 模块用于识别和执行类似交互式 Python 会话的代码片段,并测试以确保输出与所描述的期望输出匹配。Doctest 最常用于测试文档。它通过检查文档字符串中的语句序列,重新执行提取出的命令,并将其与文档字符串中的命令行输入进行比较。默认情况下,在使用 Doctest 模块时,当测试用例通过时不显示输出。但是,可以使用 Doctest 运行程序中的选项修改此行为。此外,Doctest 与 Python Unittest 框架的兼容性,使我们能够将 Doctest 作为常规 Unittest 测试用例执行。如果您想了解更多关于 Doctest 模块的信息,可以参考我们的 Python Doctest 模块 教程。

Unittest 简介

Unittest 框架提供了额外的选项,在执行测试用例时生成统计报告,包括通过和失败的测试用例数量。在 Unittest 中,类中创建的方法管理测试。它支持在测试时进行设置、自动化和关闭代码。它具有各种内置的丰富功能,包括生成器和组装管理器,这些功能不包含在 Doctest 中。

由于 Unittest 遵循面向对象的方法,因此它更适合在非生产环境中测试基于类的方法。另一方面,持续交付工具,如 Jenkins 或 Travis CI,则更适合生产环境。在接下来的部分中,我们将演示处理员工信息及其薪资上的 efs 的实际代码示例,并使用 Doctest 和 Unittest 进行测试。在进行测试后,我们将评估结果并提出进一步增强测试过程的建议。

编码示例 – 使用 Unittest

让我们实现 Employee 类并创建一些方法来使用 Unittest 进行测试。

以下是示例代码:

class Employee:
    """A sample employee class"""

    epf = 0.95

    def __init__(self, first_name, last_name, salary):
        self.first_name = first_name
        self.last_name = last_name
        self.salary = salary

    @property
    def employee_mail(self):
        return f'{self.first_name}.{self.last_name}@email.com'

    @property
    def employee_fullname(self):
        return f'{self.first_name} {self.last_name}'

    def apply_epf(self):
        self.salary = int(self.salary * self.epf)

在上面的代码中,我们从 Python 包中导入 unittest,并导入 Employee 类。在 TestEmployee 类中,我们使用了 unittest 中的 “TestCase”,它允许我们验证每组输入的特定输出。然后,我们使用以 “test” 前缀开头的方法设计了三个不同的测试。在测试运行器中,”test” 前缀表示标识要作为测试运行的方法。

test_employee.py

import unittest
from employee import Employee

class TestEmployee(unittest.TestCase):

    def setUp(self):
        self.employee_1 = Employee('Peter', 'Parker', 500000)
        self.employee_2 = Employee('Tony', 'Stark', 300000)

    def test_employee_mail(self):
        self.assertEqual(self.employee_1.employee_mail, 'Peter.Parker@email.com')
        self.assertEqual(self.employee_2.employee_mail, 'Tony.Stark@email.com')

    def test_employee_fullname(self):
        self.assertEqual(self.employee_1.employee_fullname, 'Peter Parker')
        self.assertEqual(self.employee_2.employee_fullname, 'Tony Stark')

    def test_apply_epf(self):
        self.employee_1.apply_epf()
        self.employee_2.apply_epf()

        self.assertEqual(self.employee_1.salary, 47500)
        self.assertEqual(self.employee_2.salary, 28500)

if __name__ == '__main__':
    unittest.main()

在上面的代码中,我们从 Python 包中导入 unittest,并导入 Employee 类。在 TestEmployee 类中,我们使用了 unittest 中的 “TestCase”,它提供了验证每组输入的特定输出的能力。然后,我们使用以 “test” 前缀开头的方法设计了三个不同的测试。在测试运行器中,”test” 前缀表示标识要作为测试运行的方法。

setUp() 方法在任何其他测试方法之前执行,在此方法中创建了客户端的实例。我们使用 assert() 方法中的 assertEqual() 方法,在每个测试方法中比较预期结果和实际结果。运行上述测试时,我们会得到以下结果。

...
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK

如果我们更改预期输出,我们将看到以下结果。

C:\Users\User\Desktop\my_project\unittest>python test_employee.py
F..
======================================================================
FAIL: test_apply_epf (__main__.TestEmployee)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_employee.py", line 22, in test_apply_epf
    self.assertEqual(self.employee_1.salary, 47500)
AssertionError: 475000 != 47500

----------------------------------------------------------------------
Ran 3 tests in 0.001s

FAILED (failures=1)

这种测试方法可以快速识别错误并帮助确定代码中错误发生的位置。通过利用 unittest 并创建涵盖所有可能情况的测试,我们可以增强程序的清晰度、功能和逻辑流程。

使用 Doctest

与 unittest 相比,使用 doctest 更简单且需要的步骤更少。尽管它易于使用,但在使用 doctest 时需要谨慎,因为它具有某些限制。

现在,让我们演示如何使用 doctest 来说明之前的示例。

示例 –

class Employee:
    """A sample employee class"""

    epf = 0.95

    def __init__(self, first_name, last_name, salary):
        self.first_name = first_name
        self.last_name = last_name
        self.salary = salary

    @property
    def employee_mail(self):
        return f'{self.first_name}.{self.last_name}@email.com'

    @property
    def employee_fullname(self):
        return f'{self.first_name} {self.last_name}'

    def apply_epf(self):
        self.salary = int(self.salary * self.epf)

现在,我们将在终端中执行以下操作。

class Employee:
    """A sample employee class
    >>> employee_1 = Employee("John", "Brad", 500000)
    >>> employee_2 = Employee("Tina", "Smith", 300000)
    >>> employee_1.employee_mail()
    'John.Brad@email.com'
    >>> employee_2.employee_mail()
    'Tina.Smith@email.com'
    >>> employee_1.employee_fullname()
    'John Brad'
    >>> employee_2.employee_fullname()
    'Tina Smith'
    >>> employee_1.apply_epf()
   4750
    >>> employee_2.apply_epf()
  2850
   """

    epf = 0.95

    def __init__(self, first_name, last_name, salary):
        self.first_name = first_name
        self.last_name = last_name
        self.salary = salary

    @property
    def employee_mail(self):
        return f'{self.first_name}.{self.last_name}@email.com'

    @property
    def employee_fullname(self):
        return f'{self.first_name} {self.last_name}'

    def apply_epf(self):
        self.salary = int(self.salary * self.epf)

我们在终端中运行 python doctest_employee -v 命令,它将返回以下输出。

输出 –

Trying:
    employee_1 = Employee("John", "Brad", 5000)
Expecting nothing
ok
Trying:
    employee_2 = Employee("Tina", "Smith", 3000)
Expecting nothing
ok
Trying:
    employee_1.employee_mail()
Expecting:
    'stash'
ok
Trying:
    employee_2.employee_mail()
Expecting:
    'stash'
ok
Trying:
    employee_1.employee_fullname()
Expecting:
    'John Brad'
ok
Trying:
    employee_2.employee_fullname()
Expecting:
    'Tina Smith'
ok
Trying:
    employee_1.apply_epf()
Expecting:
    4750
ok
Trying:
    employee_2.apply_epf()
Expecting:
    2850
ok
5 items had no tests:
    __main__
    __main__.employee.__init__
    __main__.employee.apply_epf
    __main__.employee.employee_fullname
    __main__.employee.employee_mail
1 items passed all tests:
   8 tests in __main__.employee
8 tests in 6 items.
8 passed and 0 failed.
Test passed.

如上面的输出所示,所有测试都已通过。Doctest 使编写可执行文档更加简单且更适合于包的文档编写,而 unittest 更适合于测试文档。

结论

本教程介绍了 doctest 和 unittest 之间的区别。unittest 框架提供了一种面向对象的方法来测试代码,包括自动化、设置和撤销测试代码等功能。另一方面,doctest 更适用于文档,因为它可以嵌入在代码的文档字符串中。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程

Python 教程