您的位置:

Python单元测试详解

一、什么是单元测试

单元测试(Unit Testing)是指对软件中的最小可测试单元进行检查和验证,最小可测试单元一般是指一个函数或方法。单元测试是程序员编写的自测代码,主要用于验证函数、过程、类等功能模块是否符合设计要求,并且可以保证后续更改不会影响已经测试通过的部分。

单元测试一般在开发周期的早期进行,对于复杂数学逻辑运算或涉及访问数据库、文件等操作的程序,单元测试显得尤为重要。

Python内置了丰富的单元测试框架,可以方便的对各种Python模块进行测试。

二、unittest模块

Python标准库中提供了一套单元测试框架——unittest模块,该模块可以方便地进行单元测试。该模块是一个基于Junit的测试框架,支持测试用例组织、执行、报告、断言和参数化等功能。

unittest作为Python官方自带的单元测试框架,使用方便而且功能强大,应用广泛。除此之外还有其他非常好的第三方单元测试框架,例如Pytest和nose等。

三、unittest的使用

1. 基本用法

unittest框架的基本流程分为三步:编写测试用例、执行测试用例、查看结果。

    import unittest

    class MyTest(unittest.TestCase):
        def test_add(self):
            self.assertEqual(1 + 2, 3)

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

以上代码是一个最简单的单元测试的例子。首先我们导入了unittest模块,然后定义了一个继承自unittest.TestCase的测试用例类MyTest,其中定义了一个测试方法test_add,该测试方法对1+2是否等于3进行断言。最后在main函数中调用unittest.main()方法即可运行单元测试。

2. 断言方法

unittest.TestCase类提供了一系列用于断言的方法,这些断言方法用于确认预期结果与实际结果是否一致。在测试中使用断言可以让我们在测试失败的时候更方便地查找出问题所在。

以下是一些常见的断言方法:

assertEqual

方法用于比较两个值是否相等。

    self.assertEqual(a, b)

assertTrue/assertFalse

方法用于判断某个表达式是否为真或是否为假。

    self.assertTrue(a > b)
    self.assertFalse(a < b)

assertIs/assertIsNot

方法用于判断两个变量是否是同一个对象。

    self.assertIs(a, b)
    self.assertIsNot(a, b)

assertIn/assertNotIn

方法用于判断某个元素是否在某个集合容器中。

    self.assertIn(a, b)
    self.assertNotIn(a, b)

3. 测试套件

测试套件(Test Suite)是将测试用例按组织结构分装起来,方便测试的一种方式。在unittest中,测试套件可以由多个测试用例和测试套件组装而成。

unittest提供了两种方式创建测试套件:TestCase类和TestSuite类。TestCase类的实例代表一个测试用例,而TestSuite类的实例则代表一个测试套件。如果测试用例较少的话,我们可以直接使用TestCase类来进行单元测试。如果测试用例过多,我们可以使用TestSuite来组织测试用例。

以下是TestSuite的例子:

    import unittest
    
    class MyTest(unittest.TestCase):
        def test_add(self):
            self.assertEqual(1 + 2, 3)
    
    class MyTestSuite(unittest.TestSuite):
        def __init__(self, tests):
            unittest.TestSuite.__init__(self)
            self.addTests(tests)
    
    if __name__ == '__main__':
        suite = MyTestSuite([
            MyTest('test_add')
        ])
        unittest.TextTestRunner().run(suite)

以上代码中,我们首先定义了一个测试类MyTest,其中包含一个测试方法test_add。然后定义了一个名为MyTestSuite的测试套件类,该类继承自unittest.TestSuite,初始化方法中将测试用例进行添加。最后我们在main函数中创建了一个测试套件,并利用TextTestRunner来运行单元测试。

4. fixtures和mocking

在进行单元测试的时候,我们可能需要进行一些操作,例如创建文件、销毁对象等。而fixtures就是用于解决这些问题的。

而mocking则是指模拟某些零件(例如:类、函数)的行为的方法,用于帮助我们进行单元测试。在Python中,我们可以使用mock模块来进行mocking操作。

以下是一个使用fixtures和mocking的例子:

    import unittest
    from unittest.mock import patch, Mock
    
    class MyTest(unittest.TestCase):
        def setUp(self):
            self.file = open('test.txt', 'w')
    
        def tearDown(self):
            self.file.close()
    
        def test_add(self):
            self.assertEqual(1 + 2, 3)
    
        @patch('builtins.print')
        def test_print(self, mock_print):
            mock_print.side_effect = Exception('Boom!')
            print('Hello World')
            mock_print.assert_called_once_with('Hello World')
            mock_print.side_effect = None
            
        def test_mock(self):
            mock = Mock()
            mock.add(1, 2, 3)
            mock.add.assert_called_once_with(1, 2, 3)
    
    if __name__ == '__main__':
        unittest.main()

以上代码中,在MyTest类中我们定义了setUp和tearDown方法,setUp方法在测试开始前会被自动调用,tearDown方法则在测试结束后被自动调用。在test_add方法中,我们进行了一个简单的运算并进行了一个断言,而在test_print方法中,我们使用了patch修饰符和side_effect参数来模拟print方法。在test_mock方法中,我们使用Mock来模拟一个前置操作和进行了一个断言。

四、Pytest

除了Python自带的unittest之外,目前最受欢迎的Python测试框架是Pytest。Pytest是一个使用起来十分简洁明了的Python测试框架,可以用于编写各种类型的测试代码:单元测试、功能测试、集成测试等。

以下为使用Pytest进行单元测试的例子:

    def test_add():
        assert 1 + 2 == 3
    
    def test_subtract():
        assert 3 - 2 == 1

以上代码中,我们定义了两个测试函数,并使用assert语句进行断言。

五、nose

nose是Python中另一种流行的测试框架,用于简化代码的编写和组织。与Pytest相似,nose可以用于自动检测测试函数,可以自动生成测试报告。

以下为使用nose进行单元测试的例子:

    def test_add():
        assert 1 + 2 == 3
    
    def test_subtract():
        assert 3 - 2 == 1

以上代码同样是定义了两个测试函数,并使用assert语句进行断言。