跳过正文

Python系列 - 单元测试

·1986 字·
Python Python开发 unittest
EZ
作者
EZ
Take it EZ!
目录
Python - 这篇文章属于一个选集。
§ 4: 本文

在 Python 中,单元测试(unit testing)是一种将代码分割成独立的小片段(单元)并进行测试的过程,以确保每个单元都能按预期工作。 Python 自带的unittest模块提供了一个全面的框架,用于编写和运行测试。

如果你听说过“测试驱动开发”(TDD:Test-Driven Development),单元测试就不陌生。

1. 编写测试用例
#

使用 unittest.TestCase 创建测试用例类,每个测试方法都以 test 开头。以下是一个简单的例子:

import unittest

# 要测试的代码
def add(a, b):
    return a + b

# 测试用例类
class TestAddFunction(unittest.TestCase):

    def test_add_integers(self):
        self.assertEqual(add(1, 2), 3)

    def test_add_floats(self):
        self.assertAlmostEqual(add(1.2, 2.3), 3.5)

    def test_add_strings(self):
        self.assertEqual(add('foo', 'bar'), 'foobar')

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

2. 断言方法
#

  • unittest 提供了多种断言方法,用于检查测试结果是否符合预期:
    • assertEqual(a, b):断言 a == b
    • assertNotEqual(a, b):断言 a != b
    • assertTrue(x):断言 xTrue
    • assertFalse(x):断言 xFalse
    • assertIs(a, b):断言 a is b
    • assertIsNot(a, b):断言 a is not b
    • assertIsNone(x):断言 xNone
    • assertIsNotNone(x):断言 x 不是 None
    • assertIn(a, b):断言 ab
    • assertNotIn(a, b):断言 a 不在 b
    • assertIsInstance(a, b):断言 ab 的实例
    • assertNotIsInstance(a, b):断言 a 不是 b 的实例

3. 组织测试
#

可以将多个测试用例组织到测试套件中,并使用测试运行器运行它们。 测试套件(Test Suite)是一种将多个测试用例(Test Case)组合在一起的方式,方便统一管理和运行。它可以包含不同测试用例类的实例,从而可以在一次运行中执行多个测试用例。

import unittest

# 测试用例类
class TestMathFunctions(unittest.TestCase):

    def test_add(self):
        self.assertEqual(add(1, 2), 3)

    def test_subtract(self):
        self.assertEqual(subtract(10, 5), 5)

# 测试套件
def suite():
    suite = unittest.TestSuite()
    suite.addTest(TestMathFunctions('test_add'))
    suite.addTest(TestMathFunctions('test_subtract'))
    return suite

if __name__ == '__main__':
    runner = unittest.TextTestRunner()
    runner.run(suite())

可以将多个测试用例类的测试方法添加到同一个测试套件中:

class TestStringMethods(unittest.TestCase):

    def test_upper(self):
        self.assertEqual('foo'.upper(), 'FOO')

    def test_isupper(self):
        self.assertTrue('FOO'.isupper())
        self.assertFalse('Foo'.isupper())

def suite():
    suite = unittest.TestSuite()
    suite.addTest(TestMathFunctions('test_add'))
    suite.addTest(TestMathFunctions('test_subtract'))
    suite.addTest(TestStringMethods('test_upper'))
    suite.addTest(TestStringMethods('test_isupper'))
    return suite
  • 这种方式允许将所有需要测试的方法都包含在一个测试套件中,从而可以统一管理和运行这些测试。
  • 使用测试套件可以有效地组织和运行多个测试用例。通过将测试用例添加到测试套件中,可以方便地批量运行测试,提高测试的效率和可维护性.

4. 设置和清理
#

在单元测试中,设置(setup)和清理(teardown)方法用于在每个测试方法执行前后进行初始化和清理操作。这有助于确保每个测试在一个干净的环境中运行,避免测试之间的相互干扰。

  • setUptearDown 方法

    • setUp 方法:在每个测试方法执行之前运行,用于初始化测试所需的环境。
    • tearDown 方法:在每个测试方法执行之后运行,用于清理 setUp 方法创建的环境。
  • 以下是一个使用 setUp 和 tearDown 方法的示例:

import unittest

class TestExample(unittest.TestCase):

    def setUp(self):
        # 在每个测试方法执行前调用
        self.test_data = [1, 2, 3, 4, 5]
        print("setUp: 初始化测试数据")

    def tearDown(self):
        # 在每个测试方法执行后调用
        self.test_data = None
        print("tearDown: 清理测试数据")

    def test_sum(self):
        # 测试求和函数
        self.assertEqual(sum(self.test_data), 15)
        print("test_sum: 测试求和函数")

    def test_max(self):
        # 测试最大值函数
        self.assertEqual(max(self.test_data), 5)
        print("test_max: 测试最大值函数")

if __name__ == '__main__':
	# 如果文件作为脚本直接运行,调用 unittest.main() 来运行所有的测试
    unittest.main()
  • 为什么使用 setUptearDown?

    1. 确保每个测试方法在独立的环境中运行:
      • setUptearDown 方法确保每个测试方法都有一个干净的起始环境,避免测试方法之间的状态影响。
    2. 减少码重复:
      • 如果多个测试方法需要相同的初始化和清理代码,可以将这些代码放在 setUptearDown 方法中,而不是在每个测试方法中重复。
    3. 提高测试的可维护性:
      • 将初始化和清理逻辑集中在 setUptearDown 方法中,使测试代码更加简洁和易于维护。
  • 更复杂的设置和清理

    • 在实际应用中,设置和清理操作可能涉及数据库连接、文件操作或网络通信等更复杂的情况。
    • unittest 模块还提供了 setUpClasstearDownClass 方法,用于在整个测试类的所有测试方法执行之前和之后进行一次性设置和清理操作。
      import unittest
      
      class TestDatabaseOperations(unittest.TestCase):
      
          @classmethod
          def setUpClass(cls):
              # 在测试类的所有测试方法执行之前调用一次
              cls.db_connection = cls.create_db_connection()
              print("setUpClass: 创建数据库连接")
      
          @classmethod
          def tearDownClass(cls):
              # 在测试类的所有测试方法执行之后调用一次
              cls.db_connection.close()
              print("tearDownClass: 关闭数据库连接")
      
          def setUp(self):
              # 在每个测试方法执行之前调用
              self.cursor = self.db_connection.cursor()
              print("setUp: 创建数据库游标")
      
          def tearDown(self):
              # 在每个测试方法执行之后调用
              self.cursor.close()
              print("tearDown: 关闭数据库游标")
      
          def test_insert(self):
              # 测试插入操作
              self.cursor.execute("INSERT INTO test_table (name) "
                                  "VALUES ('test')")
              self.db_connection.commit()
              print("test_insert: 测试插入操作")
      
          def test_query(self):
              # 测试查询操作
              self.cursor.execute("SELECT * FROM test_table")
              result = self.cursor.fetchall()
              self.assertGreater(len(result), 0)
              print("test_query: 测试查询操作")
      
          @staticmethod
          def create_db_connection():
              # 假设返回一个数据库连接对象
              return DummyDBConnection()
      
      class DummyDBConnection:
          # 一个模拟的数据库连接类,用于示例
          def cursor(self):
              return DummyCursor()
      
          def close(self):
              print("DummyDBConnection: 连接已关闭")
      
      class DummyCursor:
          # 一个模拟的数据库游标类,用于示例
          def execute(self, query):
              print(f"DummyCursor: 执行查询: {query}")
      
          def fetchall(self):
              return [('test',)]
      
          def close(self):
              print("DummyCursor: 游标已关闭")
      
      if __name__ == '__main__':
          unittest.main()
      
    • setUpClasstearDownClass 方法:
      • setUpClass:在测试类的所有测试方法执行之前调用一次,用于创建数据库连接。
      • tearDownClass:在测试类的所有测试方法执行之后调用一次,用于关闭数据库连接。
    • setUptearDown 方法:
      • setUp:在每个测试方法执行之前调用,用于创建数据库游标。
      • tearDown:在每个测试方法执行之后调用,用于关闭数据库游标。
    • 测试方法 test_inserttest_query
      • test_insert:测试插入操作,执行插入语句并提交事务。
      • test_query:测试查询操作,执行查询语句并验证结果。
    • 通过使用 setUpClasstearDownClasssetUptearDown 方法,可以更好地管理复杂的测试环境,确保测试代码的简洁性和可维护性。

5. 参考资料
#

Python - 这篇文章属于一个选集。
§ 4: 本文

相关文章

Python系列 - 递归函数
·2425 字
Python 递归 Python算法
如果一个函数在内部调用自身本身,那么这个函数就是递归函数。
Python系列 - 动态规划
·2966 字
Python 动态规划 Python算法
1. 动态规划介绍 # 动态规划(Dynamic Programmin
Python系列 - 滑动窗口
·1059 字
Python 滑动窗口 Python算法
滑动窗口是一种常用于数组或字符串的算法技巧,它通过在数组或字符串上维护一个窗口。
Spark系列 - 数据转换(I)
·2750 字
大数据 Spark DataFrame SparkSQL Transformations
本章主要讨论 Spark 的数据转换。
Spark系列 - 数据存储
·1316 字
大数据 Spark DataFrame SparkSQL 分布式数据库
本章主要讨论 pySpark 的数据存储。
Spark系列 - 数据读取
·2470 字
大数据 Spark DataFrame SparkSQL 分布式数据库
本章主要讨论 pySpark 的数据读取。