[转]单元测试框架unittest之subTest()

 

一、摘要

 

假设我们有一组测试方法差别非常小,比如仅仅是所需要的参数有少许变化时,我们的自动化测试如何进行?unittest框架为这种场景提供了一种方式,它允许我们用subTest()上下文管理器在一个测试方法内识别这种细小的不同,这么说可能有点晕

 

实际上我觉得还不如直接看代码更能清晰的表达它为我们的测试带来了什么

 

二、代码实例

 

# coding:utf-8
import unittest


class NumbersTest(unittest.TestCase):

    def test_even(self):
        """
        使用subTest上下文管理器,区分细小的变化
        取模运算,返回除法的余数,但是参数是0到5的整数,没必要单独写方法
        """
        for i in range(0, 6):
            with self.subTest(i=i):
                self.assertEqual(i % 2, 0)


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

 

执行这段代码的结果会是:

 

SubTest failure: Traceback (most recent call last):
  File "C:\Python37\lib\unittest\case.py", line 59, in testPartExecutor
    yield
  File "C:\Python37\lib\unittest\case.py", line 533, in subTest
    yield
  File "D:\Programs\Python\Demo\unittest4\subtestDemo.py", line 14, in test_even
    self.assertEqual(i % 2, 0)
  File "C:\Program Files\JetBrains\PyCharm Community Edition 2018.3.1\helpers\pycharm\teamcity\diff_tools.py", line 39, in _patched_equals
    raise native_error
  File "C:\Program Files\JetBrains\PyCharm Community Edition 2018.3.1\helpers\pycharm\teamcity\diff_tools.py", line 32, in _patched_equals
    old(self, first, second, msg)
  File "C:\Python37\lib\unittest\case.py", line 839, in assertEqual
    assertion_func(first, second, msg=msg)
  File "C:\Python37\lib\unittest\case.py", line 832, in _baseAssertEqual
    raise self.failureException(msg)
AssertionError: 1 != 0

SubTest failure: Traceback (most recent call last):
  File "C:\Python37\lib\unittest\case.py", line 59, in testPartExecutor
    yield
  File "C:\Python37\lib\unittest\case.py", line 533, in subTest
    yield
  File "D:\Programs\Python\Demo\unittest4\subtestDemo.py", line 14, in test_even
    self.assertEqual(i % 2, 0)
  File "C:\Program Files\JetBrains\PyCharm Community Edition 2018.3.1\helpers\pycharm\teamcity\diff_tools.py", line 39, in _patched_equals
    raise native_error
  File "C:\Program Files\JetBrains\PyCharm Community Edition 2018.3.1\helpers\pycharm\teamcity\diff_tools.py", line 32, in _patched_equals
    old(self, first, second, msg)
  File "C:\Python37\lib\unittest\case.py", line 839, in assertEqual
    assertion_func(first, second, msg=msg)
  File "C:\Python37\lib\unittest\case.py", line 832, in _baseAssertEqual
    raise self.failureException(msg)
AssertionError: 1 != 0

SubTest failure: Traceback (most recent call last):
  File "C:\Python37\lib\unittest\case.py", line 59, in testPartExecutor
    yield
  File "C:\Python37\lib\unittest\case.py", line 533, in subTest
    yield
  File "D:\Programs\Python\Demo\unittest4\subtestDemo.py", line 14, in test_even
    self.assertEqual(i % 2, 0)
  File "C:\Program Files\JetBrains\PyCharm Community Edition 2018.3.1\helpers\pycharm\teamcity\diff_tools.py", line 39, in _patched_equals
    raise native_error
  File "C:\Program Files\JetBrains\PyCharm Community Edition 2018.3.1\helpers\pycharm\teamcity\diff_tools.py", line 32, in _patched_equals
    old(self, first, second, msg)
  File "C:\Python37\lib\unittest\case.py", line 839, in assertEqual
    assertion_func(first, second, msg=msg)
  File "C:\Python37\lib\unittest\case.py", line 832, in _baseAssertEqual
    raise self.failureException(msg)
AssertionError: 1 != 0


One or more subtests failed
Failed subtests list: (i=1), (i=3), (i=5)


Ran 1 test in 0.020s

FAILED (failures=3)

Process finished with exit code 1
 

 

而如果我们不使用subTest(), 只是写个简单的循环去断言,当程序执行到第一个断言失败时就会终止了,后边可能还有断言能够成功的也就不会被执行了

 

# coding:utf-8
import unittest


class NumbersTest(unittest.TestCase):

    def test_even(self):
        for i in range(0, 6):
            # with self.subTest(i=i):
            print("当前参数是:%d" % i)
            self.assertEqual(i % 2, 0)


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

 

执行结果会是:

 

当前参数是:0
当前参数是:1


Ran 1 test in 0.010s

FAILED (failures=1)


0 != 1

Expected :1
Actual   :0
 <Click to see difference>

Traceback (most recent call last):
  File "C:\Program Files\JetBrains\PyCharm Community Edition 2018.3.1\helpers\pycharm\teamcity\diff_tools.py", line 32, in _patched_equals
    old(self, first, second, msg)
  File "C:\Python37\lib\unittest\case.py", line 839, in assertEqual
    assertion_func(first, second, msg=msg)
  File "C:\Python37\lib\unittest\case.py", line 832, in _baseAssertEqual
    raise self.failureException(msg)
AssertionError: 1 != 0

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Python37\lib\unittest\case.py", line 59, in testPartExecutor
    yield
  File "C:\Python37\lib\unittest\case.py", line 615, in run
    testMethod()
  File "D:\Programs\Python\Demo\unittest4\subtestDemo.py", line 15, in test_even
    self.assertEqual(i % 2, 0)


Process finished with exit code 1
 


---------------------
作者:__davieyang__
来源:CSDN
原文:https://blog.csdn.net/dawei_yang000000/article/details/86327875
版权声明:本文为作者原创文章,转载请附上博文链接!

<think>好的,用户之前已经询问过如何在pytest中实现子测试用例,我回答了使用参数化测试的方法,比如@parametrize和动态生成参数。现在用户的新问题是:“pytest框架subtest的方法吗”。看起来用户可能在使用过unittestsubTest之后,想了解pytest是否有类似的功能。 首先,我需要确认pytest是否原生支持类似unittestsubTest。根据我的知识,pytest本身没有直接的subTest方法,但可以通过其他方式实现类似的效果。比如,使用参数化测试,或者结合上下文管理器。用户可能希望在一个测试用例中运行多个子测试,即使其中一个失败,其他子测试也能继续执行,并分别报告结果,这正是unittestsubTest所做的。 接下来,我需要回忆是否有第三方库或者pytest插件提供类似subTest的功能。例如,pytest-subtests是一个第三方插件,它提供了类似的功能。需要检查这个插件的使用方法,并确认其兼容性和安装方式。 此外,用户可能想知道如何在不使用插件的情况下,用pytest原生方法模拟subTest的行为。比如,使用循环遍历测试数据,结合pytest的断言和异常处理,让每个子测试失败后继续执行,并收集所有错误信息。这可能涉及到在测试函数内部处理异常,并生成多个断言结果。 然后,我需要比较参数化测试和subTest的异同。参数化测试会生成多个独立的测试用例,每个用例在报告中单独显示,而subTest则是在单个测试用例中运行多个子测试,失败时继续执行。用户可能希望根据不同的场景选择合适的方法。例如,参数化适合明确分隔的测试用例,而subTest适合在同一个测试方法中处理多个相关检查,减少重复代码。 还要考虑用户可能遇到的问题,比如他们是否了解参数化测试的局限性,或者是否在寻找更灵活的动态子测试生成方式。比如,当测试数据需要动态生成或在运行时才能确定时,参数化可能不够灵活,这时候subTest或插件可能更适合。 需要确保回答准确,指出pytest本身没有内置的subTest,但有替代方案,如参数化测试、第三方插件pytest-subtests,或者自定义实现循环和异常处理。同时,要说明不同方法的优缺点,帮助用户根据具体需求选择最佳方案。 最后,总结各种方法的适用场景,确保用户能够理解何时使用参数化,何时需要subTest类似的功能,并提供示例代码帮助用户快速上手。</think>在 pytest 框架中,**原生没有直接等同于 `unittest.subTest` 的语法**,但可以通过以下两种方式实现类似效果: --- ### 方法 1:通过 `pytest-subtests` 插件(推荐) 第三方插件 `pytest-subtests` 提供了类似 `unittest.subTest` 的功能,允许在单个测试用例中运行多个子测试,失败后继续执行后续子测试。 #### 步骤: 1. **安装插件**: ```bash pip install pytest-subtests ``` 2. **使用 `subtests` Fixture**: ```python def test_with_subtests(subtests): for i in range(5): with subtests.test(msg="子测试", i=i): assert i % 2 == 0 # 仅验证偶数 ``` 3. **运行结果**: ``` test_subtest.py::test_with_subtests SUBPASS .../test_subtest.py::test_with_subtests 子测试 (i=0) SUBFAIL .../test_subtest.py::test_with_subtests 子测试 (i=1) SUBPASS .../test_subtest.py::test_with_subtests 子测试 (i=2) SUBFAIL .../test_subtest.py::test_with_subtests 子测试 (i=3) SUBPASS .../test_subtest.py::test_with_subtests 子测试 (i=4) ``` - 失败子测试不会中断整体测试 - 报告会明确标记每个子测试的状态 --- ### 方法 2:原生 pytest 参数化 + 自定义逻辑 通过 `@pytest.mark.parametrize` 生成独立测试用例,模拟子测试效果。 #### 示例: ```python import pytest test_data = [ {"input": 1, "expected": 2}, {"input": 2, "expected": 4}, {"input": 3, "expected": 6}, ] @pytest.mark.parametrize("data", test_data, ids=lambda d: f"input={d['input']}") def test_multiple_cases(data): assert data["input"] * 2 == data["expected"] ``` #### 特点: - 每个参数组合生成独立测试用例 - 失败时不影响其他用例 - 缺点:无法在同一个测试函数内共享上下文 --- ### 与 `unittest.subTest` 的关键区别 | 特性 | `unittest.subTest` | pytest 方案 | |---------------------|-----------------------------|-----------------------------| | **执行粒度** | 子测试(共享测试方法上下文) | 独立测试用例或插件控制的子测试 | | **错误处理** | 失败后继续执行后续子测试 | 参数化方案默认独立失败 | | **报告清晰度** | 需要手动标记子测试名称 | 自动生成独立用例ID | | **上下文共享** | 共享测试方法内的变量 | 参数化方案需通过参数传递 | --- ### 如何选择? 1. **需要子测试间共享上下文** → 使用 `pytest-subtests` 2. **需独立报告每个子测试** → 使用原生参数化 3. **动态生成测试数据** → 组合 `pytest_generate_tests` 钩子 --- ### 总结 - **原生不支持**:pytest 没有内置的 `subTest` 方法 - **替代方案**: - 优先使用 `pytest-subtests` 插件(最接近 `subTest` 的行为) - 简单场景用参数化测试(`@pytest.mark.parametrize`) - **核心优势**:通过插件或参数化保持测试隔离性,同时灵活管理大量子用例
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值