Unittest学习

优点

  • python内嵌的测试框架,无需安装即可使用
  • 执行多个case时,互不干扰,即使有一个case失败了,也不影响其余的case执行。

组成部分

  1. TestCase:测试用例类,编写的用例类需要继承该类。
  2. TestSuite:测试集,可以添加多个case,然后以添加的顺序执行用例。
  3. TestLoader:测试加载器,可以加载一类case,然后添加到TestSuite中。
  4. TextTestRunner:用例执行器,生成简单文本显示执行结果,一般以TestSuite为单位来执行用例。
  5. TestResult:测试结果。

实例:

1.

新建一个文件叫:case.py

import unittest

class study(unittest.TestCase):
    def setUp(self):
        print('测试开始前的准备函数')
    def test_01(self):
        print('测试用例01')
    def test_02(self):
        print('测试用例02')
    def tearDown(self):
        print('测试结束后的善后函数')

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

然后执行python case.py就可得到:

测试开始前的准备函数
测试用例01
测试结束后的善后函数
.测试开始前的准备函数
测试用例02
测试结束后的善后函数
.
----------------------------------------------------------------------
Ran 2 tests in 0.005s

OK

setUp()tearDown() 函数在每一个用例执行前都执行一遍。如果只需要在类层面执行一次这两种函数,可以使用 setUpClass()tearDownClass 函数,不过在使用时需要用@classmethod修饰。
例如:

import unittest

class study(unittest.TestCase):
    @classmethod
    def setUpClass(self):
        print('测试开始前的准备函数,类层面')
    def test_01(self):
        print('测试用例01')
    def test_02(self):
        print(‘测试用例02')
    @classmethod
    def tearDownClass(self):
        print('测试结束后的善后函数,类层面')

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

执行结果:

测试开始前的准备函数,类层面
测试用例01
.测试用例02
.测试结束后的善后函数,类层面

----------------------------------------------------------------------
Ran 2 tests in 0.002s

OK

2.

将刚才case.py中的两个测试用例添加到另一个文件的TestSuite中并执行:
case.py文件所在目录下新建一个文件run.py,内容为:

import unittest
from case import study
# 实例化
suite = unittest.TestSuite()
# 列表形式组合用例
case = [study('test_02'), study('test_01')]
suite.addTests(case)
# 实例化
runner = unittest.TextTestRunner()
runner.run(suite)

执行python run.py,结果为:

测试开始前的准备函数,类层面
测试用例02
.测试用例01
.测试结束后的善后函数,类层面

----------------------------------------------------------------------
Ran 2 tests in 0.002s

OK

可以看到,用例的执行顺序也改变了。
suite添加用例还可以以下列方式进行:

suite.addTest(study('test_02'))
suite.addTest(study('test_01'))

不过这种方式比较繁琐。

3.

可以发现,上述的添加case的方法只适合与用例较少的情况,在用例非常多的情况下,为了方便添加,就需要使用TestLoader,即用例加载器。
接下来就对run.py进行改造:

import unittest
from case import study

suite = unittest.TestSuite()
loader = unittest.TestLoader().loadTestsFromTestCase(study)
suite.addTests(loader)
runner = unittest.TextTestRunner()
runner.run(suite)

执行结果:

测试开始前的准备函数,类层面
测试用例01
.测试用例02
.测试结束后的善后函数,类层面

----------------------------------------------------------------------
Ran 2 tests in 0.002s

OK

loader查找case的方式还有如下几种:

import case
loader = unittest.TestLoader().loadTestsFromModule(case)
# 感觉也是每次添加一个用例,不推荐
loader = unittest.TestLoader().loadTestsFromName('case.study.test_01')
# 如果有多个case文件,case, case1……,可以使用discover
loader = unittest.defaultTestLoader.discover(start_dir='path', pattern='case*.py')
# path即指这些case文件所在的绝对路径

4.

TextTestRunner在上面的例子中有所体现,基本上就是:

runner = unittest.TextTestRunner()
runner.run(suite)

这样的用法。

断言

测试用例执行需要用断言来判断是否成功
新建一个example01,内容:

import unittest

a, b = 1, 2
class study01(unittest.TestCase):
    def setUp(self):
        print('----start----')
    def test_01(self):
        self.assertEqual(a, b, 'a < b')
    def tearDown(self):
        print('-----end-----')

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

执行结果:

----start----
-----end-----
F
======================================================================
FAIL: test_01 (__main__.study01)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\weilong\Pworkspace\Test_study\Unitest_simple\example01.py", line 8, in test_01
    self.assertEqual(a, b, 'a < b')
AssertionError: 1 != 2 : a < b

----------------------------------------------------------------------
Ran 1 test in 0.002s

FAILED (failures=1)

可以看到,如果断言失败,会显示预留的信息。
断言的类型:

assertEqual(a,b) a==b 值是否相等
aassertNotEqual(a,b) a!=b 值是否不相等
aasserIs(a,b) a is b 值是否相同
aassertIsNot(a,b) a is not b 值是否不同
assertIn(a,b) a in b a是否包含b
assertNotIn(a,b) a not in b a是否不包含b
ssertTrue(a) bool(a) is true 是否为真
assertFalse(a) bool(a)is false 是否为假
assertIsNone(a) a is None 是否为空
assertIsNotNone(a) a is None 是否不为空
assertIsInstance(a,b) Instance(a,b) a与b的数据类型一样
assertNotIsInstance(a) not Instance(a,b) a与b的数据类型不一样

跳过

有时候,因为环境或者需要等因素,一些用例需要进行跳过处理,这时候就需要用到@unittest.skip()来修饰用例。
改写example01:

import unittest

a, b = 1, 2
class study01(unittest.TestCase):
    def setUp(self):
        print('----start----')
    @unittest.skip('a != b')
    def test_01(self):
        self.assertEqual(a, b, 'a < b')
    def test_02(self):
        self.assertNotEqual(a, b, 'a = b')
    def tearDown(self):
        print('-----end-----')

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

执行结果:

s----start----
-----end-----
.
----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK (skipped=1)

可以看懂跳过了一个测试用例。然后,skip()括号内的是reason,虽然执行结果没有显示,但其实是一个必要的参数,如果没填入会报错。
skip还有一种方式:

@unittest.skipIf(option, reason)

数据驱动

大量不同的数据进行相同的逻辑函数测试,可以用ddt模块,安装

pip install ddt

使用:

import unittest
from ddt import ddt, data

@ddt
class study02(unittest.TestCase):
    @data('a', 'b', 'c')
    # 即将a, b, c轮流与a比较,不相同的断言失败
    def test_01(self, text):
        self.assertEqual('a', text)
    
if __name__ == '__main__':
    unittest.main()

此外,还有以文件形式存储的数据驱动

import unittest
from ddt import ddt, file_data

@ddt
class study03(unittest.TestCase):
    @file_data('filepath')
    # **指字典形式。 *指列表形式
    def test_01(self, **text):
        print(text)
    
if __name__ == '__main__':
    unittest.main()

文件格式为jsonyaml
对于excelcsvtxt文件,需要先进行数据的读取。

unpack修饰会将列表数据拆分到参数中,不然多个参数时会将数据只传到第一个参数中。

测试报告

利用html-testRunner插件生成html测试报告,安装:

pip install html-testRunner

使用:

import unittest
from case import study
from HtmlTestRunner import HTMLTestRunner

suite = unittest.TestSuite()

loader = unittest.TestLoader().loadTestsFromTestCase(study)
suite.addTests(loader)
# 在该文件目录下创建report目录,将报告存入其中
runner = HTMLTestRunner(output='report')
runner.run(suite)

带中文的测试信息会产生乱码,所以要对该插件的result.py进行修改,
def generate_file(self, testRunner, report_name, report):方法下的with open(path_file, 'w') as report_file:改成with open(path_file, 'w',encoding='utf-8') as report_file:,这样就可以得到带中文的测试报告了。

<think>嗯,用户想学习如何使用unittest进行Python单元测试的示例教程。首先,我需要整理一下unittest的基本概念和步骤。记得按照用户的要求,行内数学公式用$...$,独立公式用$$...$$,不过这部分可能用不上,因为主要是代码和文字说明。然后要引用相关的站内资料,比如引用[1]、[2]这些。 首先,我应该从安装开始,不过unittestPython自带的,可能不需要安装。接下来是基本结构,比如测试类继承TestCase,测试方法以test_开头。需要给出一个简单的示例代码,比如测试字符串方法upper(),这样用户容易理解。 然后,要说明如何运行测试,可以通过命令行或者IDE,比如在PyCharm中运行。还要提到常用的断言方法,比如assertEqual、assertTrue等,这部分可以引用引用[4]中的例子。 接下来,可能需要讲测试夹具,也就是setUp和tearDown方法,这样用户知道如何初始化和清理资源。这部分可以结合引用[4]里的测试类结构来说明。 另外,用户可能需要知道如何组织多个测试用例,比如创建不同的测试类,或者使用测试套件。可以引用引用[5]提到的自动执行test_开头的方法。 最后,生成相关问题,比如性能分析、优化方法、应用场景,但这里要针对单元测试,可能需要调整问题,比如如何组织测试用例、断言方法有哪些、与pytest的区别等。还要确保每个引用都正确标注,比如在提到自动运行测试方法时引用[5],在测试类结构时引用[4]等。 需要检查示例代码是否正确,比如是否有语法错误,是否使用了unittest.TestCase,以及断言方法是否正确。同时,确保回答结构清晰,分步骤说明,让用户能够一步步跟着操作。可能还需要提到unittest和pytest的区别,引用引用[3]中的内容,比如pytest更简洁,但unittest是标准库的一部分。 总的来说,回答需要涵盖基本用法、示例代码、运行方法、断言方法、测试夹具,以及常见问题,同时正确引用提供的资料。要避免使用Markdown格式,但用户的问题里允许代码块,所以示例代码用正确的格式展示。最后生成的相关问题要围绕单元测试的使用,帮助用户进一步学习。</think>### 如何使用unittest进行Python单元测试 #### 一、基本概念与安装 1. **unittest简介** unittestPython标准库内置的单元测试框架,提供测试用例组织、断言验证、测试夹具(setup/teardown)等功能[^2]。 2. **无需安装** 直接通过`import unittest`即可使用。 #### 二、编写第一个单元测试 ##### 步骤1:创建被测函数 假设需要测试一个字符串处理函数: ```python # string_utils.py def capitalize_string(s): return s.capitalize() ``` ##### 步骤2:编写测试类 测试类需继承`unittest.TestCase`,测试方法以`test_`开头: ```python # test_string_utils.py import unittest from string_utils import capitalize_string class TestStringUtils(unittest.TestCase): def test_capitalize_normal_input(self): result = capitalize_string("hello") self.assertEqual(result, "Hello") # 验证结果是否符合预期 def test_capitalize_empty_input(self): result = capitalize_string("") self.assertEqual(result, "") # 边界条件测试 ``` ##### 步骤3:运行测试 - **命令行运行**: ```bash python -m unittest test_string_utils.py ``` - **PyCharm运行**: 右键点击测试文件选择`Run 'Unittests in test_string_utils.py'`[^3]。 #### 三、核心功能详解 1. **断言方法** unittest提供多种断言方法验证结果: - `self.assertEqual(a, b)`:验证$a = b$ - `self.assertTrue(x)`:验证$x$为真 - `self.assertRaises(ErrorType)`:验证是否触发特定异常 示例: ```python def test_division_by_zero(self): with self.assertRaises(ZeroDivisionError): 1 / 0 # 验证触发除零异常 ``` 2. **测试夹具(setup/teardown)** - `setUp()`:每个测试方法执行前调用,用于初始化资源。 - `tearDown()`:每个测试方法执行后调用,用于清理资源。 示例: ```python class TestDatabase(unittest.TestCase): def setUp(self): self.conn = create_database_connection() # 初始化数据库连接 def test_query_data(self): data = self.conn.query("SELECT * FROM users") self.assertGreater(len(data), 0) def tearDown(self): self.conn.close() # 关闭连接 ``` #### 四、高级用法 1. **测试套件(Test Suite)** 手动组合多个测试类或方法: ```python def suite(): suite = unittest.TestSuite() suite.addTest(TestStringUtils('test_capitalize_normal_input')) suite.addTest(TestDatabase('test_query_data')) return suite if __name__ == '__main__': runner = unittest.TextTestRunner() runner.run(suite()) ``` 2. **跳过测试** 使用装饰器跳过特定测试: ```python @unittest.skip("功能待实现") def test_unimplemented_feature(self): pass ``` #### 五、与pytest对比 - **unittest优势**:Python原生支持,适合简单场景。 - **pytest优势**:语法更简洁,支持参数化测试,适合复杂项目。 ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值