新知一下
海量新知
5 9 7 0 4 4 3

Python 中的单元测试框架和测试驱动开发 (TDD)

磐创AI | 人工智能领域前沿自媒体。 2021/10/14 11:07

作者 | lavanya@99

编译 | Flin

来源 | analyticsvidhya

概述

运行数据项目需要大量时间。糟糕的数据会导致糟糕的判断。在数据科学和数据工程项目中运行 单元测试可 确保数据质量。

目录

  1. 介绍

  2. 单元测试的重要性

  3. 单元测试框架的一些基本功能

  4. square 和 double 函数的单元测试

  5. 命令行界面

  6. 跳过单元测试和预期失败

  7. 使用子集测试迭代

介绍

单元测试是一种系统的、可重复的测试代码的方式。它是一种验证代码单元是否按设计运行的方法。

单元是应用程序的一个小的可测试部分,通常是功能和模块。我们将使用单元测试库,这是一个已安装的 Python 模块,提供包含测试功能的框架。

在代码开发过程中,我们必须测试每个单元。如果测试失败,开发人员会确定原因并修复它。

一旦单元测试通过,我们将在持续集成交付测试服务器环境中测试单元。如果单元测试未通过服务器测试,它会将代码返回给开发人员以确定原因并修复,然后流程重新开始。一旦该单元通过服务器测试,我们就会将该单元合并到最终的主代码库中。

新知达人, Python 中的单元测试框架和测试驱动开发 (TDD)

单元测试的重要性

我们都擅长编码和解决问题。虽然涉及复杂性和错误处理,但我们应该检查代码是否有任何错误,因此我们需要一个检查点来检查我们的代码在哪里可以通过所有测试用例。

为此,我们使用了许多包附带的 Python 单元测试框架。其中之一是 Unittest 框架,它预装了该语言。我们可以预测我们的代码可能会失败或产生问题的场景。单元测试使你的代码面向未来。即使我们无法预料到每种情况,你仍然可以处理大多数情况。

单元测试框架的一些基本功能

子类化 unittest.TestCase 产生一个测试用例。名称以字母 test 开头的方法定义了单独的测试。这个命名标准向测试运行者展示了哪些方法构成了测试。

测试三字符串方法的简短脚本:

import unittest

class StringMethodstest(unittest.TestCase):

    def test_upper(self):

        self.assertEqual('python'.upper(), 'PYTHON')

    def test_isupper(self):

        self.assertTrue('PYTHON'.isupper())

        self.assertFalse('python'.isupper())

    def test_split(self):

        s = 'hello python'

        self.assertEqual(s.split(), ['hello''python'])

        with self.assertRaises(TypeError):

            s.split(2)

if __name__ == '__main__':

    unittest.main()

每个测试的核心是对 assertEqual() 的调用 它检查预期的结果; assertTrue() assertFalse() ,用于验证条件;或 assertRaises() ,它验证是否引发了指定的异常。我们使用这些技术代替 assert 语句,以允许测试运行器编译所有测试结果并提供报告。

最后一部分演示了运行测试的简单方法。 unittest.main() 为测试脚本提供了一个命令行界面。从命令行执行时,上述脚本给出以下输出:

打开终端并输入命令:

python -m unittest -v test

新知达人, Python 中的单元测试框架和测试驱动开发 (TDD)

python函数中的单元测试示例:

首先,请注意文件名附加了 test 以将其标识为单元测试文件。

接下来,我们导入单元测试库。

然后我们导入我们要测试的函数。现在我们可以构建我们的单元测试类。

首先,我们创建一个测试类。命名你正在测试的文件并为类名附加测试是一个很好的命名约定。

接下来,我们让我们的类继承单元测试库的测试用例类,这允许我们的类使用测试类中的方法。

示例 1:

首先,编写一个python函数来查找数字的阶乘并将其命名为mymodule1.py:

def factorial(number):

    if number == 0:

        return 1

    else:

        return number * factorial(number-1)

现在我们在 test_mymodule 类中为每个要测试的函数创建一个函数。

我们通过在要测试的函数前添加测试来命名函数。请注意,此步骤是强制性的,因为只有以测试开始的函数才会被处理。

最后,我们可以开始创建测试用例。我们可以使用assertEqual()函数来做到这一点。让我们仔细看看测试类中使用的断言相等。Assert Equal 比较两个值并确定它们是否相等。该方法可以检查函数是否返回正确的值。

创建一个 test_mymodule1.py 来测试函数是否返回了预期的输出,

import unittest

from mymodule1 import factorial

class TestFactorial(unittest.TestCase):

    def test1(self):

        self.assertEqual(factorial(4), 24# test when 4 is given as input the output is 24.

        self.assertEqual(factorial(5), 120# test when 5 is given as input the output is 120.

        self.assertEqual(factorial(0), 1)

unittest.main()

在我们确定了程序的功能之后,我们就可以针对这四种场景编写测试用例了。

  • 当输入 4 时,阶乘必须是 24。

  • 当输入 5 时,阶乘必须是 120。

  • 当输入 0 时,阶乘必须为 1。

  • 当输入 2 时,输出不得为 2。

首先,我们评估函数,然后比较两个值以查看它们是否相等。

运行我们的测试文件后,我们将得到这样的输出。

  1. 最后一行中的 OK 表示所有测试都成功通过。

  2. 最后一行的 FAILED 表明至少有一个测试失败,python 打印出哪些测试或测试失败。

    ![](http://qiniu.aihubs.net/12252unit 1 - 2.png)

如果函数实现不正确会怎样?

import unittest

from mymodule1 import factorial

class TestFactorial(unittest.TestCase):

    def test1(self):

        self.assertEqual(factorial(4), 24)

        self.assertEqual(factorial(5), 120)

        self.assertNotEqual(factorial(2), 2)

unittest.main()

考虑以下函数,其中的阶乘实现是不正确的,因为它对数字进行立方而不是求阶乘。

assertEqual() 的评估看起来像这样,失败并反馈失败的断言。

![](http://qiniu.aihubs.net/21752unit 1.png)考虑我们正在编写一个对数字进行平方和加倍的函数:

def square(number):

    """

    This function returns the square of a number.

    """

    return number ** 2

def double(number):

    """

    This function returns twice the value of a number.

    """

    return number * 2

square 和 double 函数的单元测试

  1. 当输入 5 时,总和必须是 25。

  2. 当输入 3.0 时,总和必须为 9.0。

  3. 当输入 -3 时,总和不得为 -9。

  4. 当输入 2 时,double 必须是 4。

  5. 当输入-3.1 时,double 必须是-6.2。

  6. 当输入 0 时,double 必须为 0。

下一步是创建一个新文件并将其命名为 test_mymodule.py。

import unittest

from mymodule import square, double, factorial

class TestSquare(unittest.TestCase): 

    def test1(self): 

        self.assertEqual(square(5), 25# test when 5 is given as input the output is 25.

        self.assertEqual(square(3.0), 9.0)  # test when 3.0 is given as input the output is 9.0.

        self.assertNotEqual(square(-3), -9)  # test when -3 is given as input the output is not -9.

class TestDouble(unittest.TestCase): 

    def test1(self): 

        self.assertEqual(double(2), 4# test when 2 is given as input the output is 4.

        self.assertEqual(double(-3.1), -6.2# test when -3.1 is given as input the output is -6.2.

        self.assertEqual(double(0), 0# test when 0 is given as input the output is 0.

unittest.main()

如果你使用的是 IDE,要运行测试,请选择 test_mymodule.py 文件并单击“播放”按钮。

![](http://qiniu.aihubs.net/74662unit 2.png)

使用命令行界面

unittest 模块可以通过命令行界面从模块类甚至单个测试方法运行测试。我们可以传入一个包含任何模块名称序列以及完全限定的类或方法名称的列表。

python -m unittest test_mymodule1 test_module2

python -m unittest test_module.TestClass

python -m unittest test_module.TestClass.test_method

测试模块也可以通过文件路径运行。它允许你使用文件名完成来定义测试模块。

python -m unittest tests/test_mymodule.py

你可以通过传入 -v 标志来运行更详细的测试:

python -m unittest -v test_mymodule

不带参数执行时,将启动测试发现(https://docs.python.org/3/library/unittest.html#unittest-test-discovery):

python -m unittest

使用以下命令获取所有可用命令行选项的列表:

python -m unittest -h

跳过测试和预期失败:

跳过测试就像使用 skip() 装饰器运行测试用例一样简单。 SkipTest() 可以从 setUp() 或测试方法中调用,也可以直接pass。

class MyTestCase(unittest.TestCase):

        @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")

    def test_windows_support(self):

        # windows specific testing code

        pass

    def test_maybe_skipped(self):

        if not external_resource_available():

            self.skipTest("external resource not available")

        # test code that depends on the external resource

        pass

顺便说一句,我们可以像方法一样跳过类:

@unittest.skip("showing class skipping")

class SkippedTestCase(unittest.TestCase):

    def test_not_run(self):

        pass

预期失败使用 expectedFailure() 装饰器返回预期失败。

class ExpectedFailureTestCase(unittest.TestCase):

    @unittest.expectedFailure

    def test_fail(self):

        self.assertEqual(10"broken")

使用子测试进行测试迭代

当你的测试之间只有微小的变化时,例如某些参数,unittest 允许你通过使用 subTest() 函数在测试方法的主体内将它们分开。

class TestNumbers(unittest.TestCase):

    def test_even(self):

        """

        Test that numbers between 0 and 6 are all even.

        """

        for i in range(07):

            with self.subTest(i=i):

                self.assertEqual(i % 20)

如果没有子测试,执行将在第一次失败后停止,并且问题将更难以识别,因为不会显示“i”的值。

感谢阅读!

更多“Python”相关内容

更多“Python”相关内容

新知精选

更多新知精选