Python 进行单元测试

单元测试是软件开发过程中的一个关键步骤,用于验证程序的各个部分(单元)是否按预期工作。Python 提供了多种进行单元测试的方法,其中最常用的框架是 unittest

一、单元测试的基本概念

1、单元测试(Unit Testing)

  • 是对软件中的最小可测试单元进行验证的过程。
  • 单元通常指函数或方法。

2、测试用例(Test Case)

  • 测试用例是对单元进行的具体测试,包含输入、预期输出以及验证逻辑。
  • 一个单元可能有多个测试用例,以确保其在不同情况下的正确性。

3、测试套件(Test Suite)

  • 测试套件是多个测试用例的集合。
  • 通过测试套件,可以批量运行多个测试用例。

4、测试执行器(Test Runner)

  • 测试执行器用于组织和执行测试用例,并报告测试结果。
二、unittest框架

unittest 是 Python 标准库中的一个模块,提供了创建和运行测试用例的功能。以下是 unittest 框架的核心组件:

1、TestCase

  • TestCase 类是所有测试用例的基类。
  • 通过继承 unittest.TestCase,可以创建自己的测试用例类。

2、assert 方法

  • TestCase 类提供了多种断言方法,用于验证测试结果是否符合预期。
  • 常用的断言方法包括 assertEqualassertTrueassertFalse 等。

3、TestSuite

  • TestSuite 类用于组织多个测试用例。

4、TestLoader

  • TestLoader 类用于查找和加载测试用例。

5、TextTestRunner

  • TextTestRunner 类用于执行测试用例,并将结果输出到控制台。
三、编写单元测试的步骤

下面通过一个具体的例子,详细讲解如何使用 unittest 编写和运行单元测试。

1、创建被测试的代码

首先,我们创建一个简单的数学运算模块 math_operations.py,其中包含两个函数:加法和除法。

python 复制代码
# math_operations.py

def add(a, b):
    return a + b

def divide(a, b):
    if b == 0:
        raise ValueError("Cannot divide by zero!")
    return a / b

2、编写测试用例

接下来,我们为 math_operations.py 编写测试用例。

python 复制代码
# test_math_operations.py
import unittest
from math_operations import add, divide

class TestMathOperations(unittest.TestCase):

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

    def test_divide(self):
        self.assertEqual(divide(10, 2), 5)
        self.assertEqual(divide(-10, 2), -5)
        self.assertRaises(ValueError, divide, 10, 0)

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

3、运行测试

保存上述文件后,在命令行中运行测试:

bash 复制代码
python test_math_operations.py

如果所有测试都通过,输出将类似于:

bash 复制代码
..
----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK
四、unittest的高级用法

1、测试套件

可以将多个测试用例组合成一个测试套件,便于一次运行多个测试。

python 复制代码
# test_suite.py
import unittest
from test_math_operations import TestMathOperations

def suite():
    suite = unittest.TestSuite()
    suite.addTest(TestMathOperations('test_add'))
    suite.addTest(TestMathOperations('test_divide'))
    return suite

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

2、设置和清理

unittest 提供了 setUptearDown 方法,用于在每个测试用例运行之前和之后进行设置和清理。

python 复制代码
# test_math_operations_with_setup.py
import unittest
from math_operations import add, divide

class TestMathOperations(unittest.TestCase):

    def setUp(self):
        print("Setting up the test environment.")

    def tearDown(self):
        print("Cleaning up after the test.")

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

    def test_divide(self):
        self.assertEqual(divide(10, 2), 5)
        self.assertEqual(divide(-10, 2), -5)
        self.assertRaises(ValueError, divide, 10, 0)

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

3、跳过测试

在某些情况下,可能需要有条件地跳过某些测试。unittest 提供了 skip 装饰器来实现这一功能。

python 复制代码
# test_skip.py
import unittest

class TestMathOperations(unittest.TestCase):

    @unittest.skip("Skipping this test")
    def test_add(self):
        self.assertEqual(add(1, 2), 3)

    @unittest.skipIf(1 == 1, "Skipping this test because 1 == 1")
    def test_divide(self):
        self.assertEqual(divide(10, 2), 5)

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

4、参数化测试

通过 unittest,可以实现参数化测试,即使用不同的参数多次运行同一测试。

python 复制代码
# test_parameterized.py
import unittest
from parameterized import parameterized
from math_operations import add

class TestMathOperations(unittest.TestCase):

    @parameterized.expand([
        (1, 2, 3),
        (-1, 1, 0),
        (-1, -1, -2),
    ])
    def test_add(self, a, b, expected):
        self.assertEqual(add(a, b), expected)

if __name__ == '__main__':
    unittest.main()
五、结合持续集成工具

单元测试是持续集成(CI)的一部分。CI 工具(如 Jenkins、Travis CI、GitHub Actions 等)可以自动运行单元测试,并在代码库发生更改时报告结果。以下是一个简单的 GitHub Actions 配置文件示例,用于在每次推送代码时运行单元测试:

XML 复制代码
# .github/workflows/python-app.yml
name: Python application

on: [push]

jobs:
  build:

    runs-on: ubuntu-latest

    strategy:
      matrix:
        python-version: [3.6, 3.7, 3.8, 3.9]

    steps:
    - uses: actions/checkout@v2
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v2
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
    - name: Run tests
      run: |
        python -m unittest discover

通过上面内容,我们了解了 Python 单元测试的基本概念、unittest 框架的使用方法以及一些高级技巧。单元测试是保证代码质量的重要手段,结合持续集成工具,可以显著提高软件开发的效率和可靠性。

相关推荐
黑金IT3 分钟前
WebSocket vs. Server-Sent Events:选择最适合你的实时数据流技术
网络·python·websocket·网络协议·fastapi
Amo Xiang4 分钟前
Python 常用模块(二):json模块
开发语言·python·json
cxylay5 分钟前
【python版】示波器输出的csv文件(时间与电压数据)如何转换为频率与幅值【方法③】
开发语言·python·示波器·频谱·csv文件·时域·频域
spiritualfood9 分钟前
联合体与枚举以及结构体补充
c语言·开发语言·c++·算法·青少年编程
sun_weitao10 分钟前
extends in javascript
开发语言·前端·javascript
血不热了14 分钟前
Qt:懒汉单例(附带单例使用和内存管理)
开发语言·qt·设计模式
u01129006424 分钟前
一种全新的webapi框架C#webmvc初步介绍
开发语言·c#
liangbm329 分钟前
数学建模笔记—— 蒙特卡罗法
笔记·python·数学建模·概率论·概率统计·三门问题·蒙特卡罗法
叫我DPT38 分钟前
Django——多apps目录情况下的app注册
python·django
Hcoco_me39 分钟前
C++ IO框架
开发语言·网络·c++