Python单元测试:10个单元测试框架的使用方法

文摘   2024-10-10 08:31   湖北  

单元测试是软件开发中不可或缺的一部分,它能够帮助开发者确保代码的质量和稳定性。Python 社区提供了多种单元测试框架,每种框架都有其独特的优势和适用场景。本文将介绍几种常见的 Python 单元测试框架,并通过实际例子帮助读者更好地理解和使用它们。

1. unittest 模块

unittest 是 Python 自带的标准库之一,它基于 Java 的 JUnit 框架设计,提供了一套完整的单元测试框架。

基本用法:

import unittest

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())

if __name__ == '__main__':
    unittest.main()
  • 这段代码定义了一个测试类 TestStringMethods,继承自 unittest.TestCase
  • test_uppertest_isupper 方法分别测试字符串的大写转换和是否全为大写的检查。
  • unittest.main() 启动测试运行器。

进阶用法:

import unittest

class TestStringMethods(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        print("这个方法只在所有测试开始前执行一次")

    def setUp(self):
        print("这个方法会在每个测试方法之前执行")
        self.test_string = "hello world"

    def test_upper(self):
        self.assertEqual(self.test_string.upper(), 'HELLO WORLD')

    def test_isupper(self):
        self.assertTrue('HELLO'.isupper())
        self.assertFalse('Hello'.isupper())

    def tearDown(self):
        print("这个方法会在每个测试方法之后执行")
        del self.test_string

    @classmethod
    def tearDownClass(cls):
        print("这个方法在所有测试结束后执行一次")

if __name__ == '__main__':
    unittest.main()
  • setUpClass 类方法在整个测试类开始前执行一次。
  • setUp 方法在每个测试方法前执行,用于准备测试数据。
  • tearDown 方法在每个测试方法后执行,用于清理测试环境。
  • tearDownClass 类方法在所有测试结束后执行一次。

2. pytest 框架

pytest 是目前非常流行的一个第三方单元测试框架,它简洁易用,扩展性强。

基本用法:

def test_upper():
    assert 'foo'.upper() == 'FOO'

def test_isupper():
    assert 'FOO'.isupper()
    assert not 'Foo'.isupper()
  • 使用 assert 断言来验证期望的结果。
  • 直接定义函数名以 test_ 开头的方法作为测试用例。

进阶用法:

import pytest

@pytest.fixture
def setup_data():
    print("setup data")
    return "hello world"

def test_upper(setup_data):
    assert setup_data.upper() == "HELLO WORLD"

def test_isupper():
    assert 'HELLO'.isupper()
    assert not 'Hello'.isupper()

def test_fixture_teardown(setup_data):
    print("teardown data")
  • @pytest.fixture 装饰器定义了一个测试夹具(fixture),可以在多个测试用例之间共享数据。
  • setup_data 函数会在 test_uppertest_fixture_teardown 方法之前执行。

3. Pytest-cov

pytest-cov 是一个用于生成代码覆盖率报告的插件,它可以与 pytest 配合使用。

安装:

pip install pytest-cov

基本用法:

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

def test_add():
    assert add(12) == 3

def test_add_negative():
    assert add(-1-1) == -2
  • 定义一个简单的 add 函数和两个测试用例。

运行测试并生成覆盖率报告:

pytest --cov=my_module
  • --cov=my_module 参数指定要生成覆盖率报告的模块。

输出示例:

============================= test session starts ==============================
platform darwin -- Python 3.10.7, pytest-7.1.2, py-1.11.0, pluggy-1.0.0
rootdir: /path/to/your/project
plugins: cov-3.0.0
collected 2 items

tests/test_my_module.py ..                                                [100%]

----------- coverage: platform darwin, python 3.10.7-final-0 -----------
Name              Stmts   Miss  Cover   Missing
-------------------------------------------------
my_module.py          1      0   100%
-------------------------------------------------
TOTAL                 1      0   100%

4. Nose2

nose2nose 的改进版,它支持更多的测试发现机制和插件。

安装:

pip install nose2

基本用法:

import unittest

class TestAdd(unittest.TestCase):
    def test_add_positive(self):
        self.assertEqual(add(12), 3)

    def test_add_negative(self):
        self.assertEqual(add(-1-1), -2)
  • 定义一个测试类 TestAdd

运行测试:

nose2

输出示例:

.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK

5. Hypothesis

hypothesis 是一个强大的参数化测试库,可以生成大量随机数据进行测试。

安装:

pip install hypothesis

基本用法:

from hypothesis import given, strategies as st
from my_module import add

@given(st.integers(), st.integers())
def test_add(a, b):
    assert add(a, b) == a + b
  • 使用 @given 装饰器定义测试函数。
  • st.integers() 生成整数类型的随机数据。

运行测试:

pytest

输出示例:

============================= test session starts ==============================
platform darwin -- Python 3.10.7, pytest-7.1.2, py-1.11.0, pluggy-1.0.0
rootdir: /path/to/your/project
plugins: hypothesis-6.44.0
collected 1 item

tests/test_my_module.py .                                               [100%]

============================== short test summary info ===============================
hypothesis passed 100 tests for test_add, 1.00% of examples were new[100%]

6. Doctest

doctest 是 Python 标准库中的一个模块,可以将文档字符串中的示例作为测试用例。

基本用法:

def add(a, b):
    """
    >>> add(1, 2)
    3
    >>> add(-1, -1)
    -2
    """

    return a + b
  • 在文档字符串中编写测试用例。

运行测试:

python -m doctest my_module.py

输出示例:

Trying:
    add(1, 2)
Expecting:
    3
ok
Trying:
    add(-1, -1)
Expecting:
    -2
ok
2 items had no tests:
    my_module
    my_module.add
1 items passed all tests:
   2 tests in my_module.add
2 tests in 1 items.
2 passed and 0 failed.
Test passed.

7. Pytest-Check

pytest-checkpytest 的一个插件,提供了一些方便的断言函数。

安装:

pip install pytest-check

基本用法:

from check import check

def test_add():
    check.equal(add(12), 3)
    check.equal(add(-1-1), -2)
  • 使用 check.equal 断言函数进行验证。

运行测试:

pytest

输出示例:

============================= test session starts ==============================
platform darwin -- Python 3.10.7, pytest-7.1.2, py-1.11.0, pluggy-1.0.0
rootdir: /path/to/your/project
plugins: check-0.2.0
collected 1 item

tests/test_my_module.py .                                               [100%]

============================== short test summary info ===============================
1 passed in 0.01s

8. Pytest-Mock

pytest-mock 是一个 pytest 插件,用于模拟对象的行为。

安装:

pip install pytest-mock

基本用法:

from my_module import some_function
import pytest

def test_some_function(mocker):
    mocker.patch('my_module.some_function', return_value=42)
    result = some_function()
    assert result == 42
  • 使用 mocker.patch 模拟 some_function 的返回值。

运行测试:

pytest

输出示例:

============================= test session starts ==============================
platform darwin -- Python 3.10.7, pytest-7.1.2, py-1.11.0, pluggy-1.0.0
rootdir: /path/to/your/project
plugins: mock-3.7.0
collected 1 item

tests/test_my_module.py .                                               [100%]

============================== short test summary info ===============================
1 passed in 0.01s

实战案例:在线购物车系统

假设我们有一个在线购物车系统,用户可以添加商品到购物车,并查看总价。我们需要编写单元测试来确保系统的正确性。

代码实现:

# shopping_cart.py

class ShoppingCart:
    def __init__(self):
        self.items = []

    def add_item(self, item_name, price, quantity=1):
        self.items.append((item_name, price, quantity))

    def get_total(self):
        total = 0
        for item in self.items:
            total += item[1] * item[2]
        return total

单元测试:

# test_shopping_cart.py

import pytest
from shopping_cart import ShoppingCart

def test_add_item():
    cart = ShoppingCart()
    cart.add_item("apple"2.02)
    assert len(cart.items) == 1

def test_get_total():
    cart = ShoppingCart()
    cart.add_item("apple"2.02)
    cart.add_item("banana"1.53)
    assert cart.get_total() == 2 * 2.0 + 3 * 1.5

运行测试:

pytest

输出示例:

============================= test session starts ==============================
platform darwin -- Python 3.10.7, pytest-7.1.2, py-1.11.0, pluggy-1.0.0
rootdir: /path/to/your/project
collected 2 items

test_shopping_cart.py ..                                               [100%]

============================== short test summary info ===============================
2 passed in 0.01s

总结

本文介绍了 Python 中常用的几种单元测试框架及其基本用法,包括 unittestpytestpytest-covnose2hypothesisdoctestpytest-checkpytest-mock。通过实战案例展示了如何使用这些框架编写有效的单元测试,帮助确保代码的质量和稳定性。



Python学习杂记
数据分析与挖掘、运筹优化、机器学习、AI 、数据可视化等。
 最新文章