Python 单元测试详解:Unittest 框架的应用与最佳实践
Python 单元测试详解:Unittest 框架的应用与最佳实践
文章目录
- Python 单元测试详解:Unittest 框架的应用与最佳实践
- 一 什么是 Unittest
- 1 不使用 Unittest 测试框架
- 2 使用 Unittest 测试框架
- 二 unittest 使用建议
- 1 先写测试 case 后写测试逻辑
- 2 测试文件以 _test.py 结尾
- 三 多个功能测试
- 四 用 Python 命令执行测试
- 五 断言 assert 常用方法
- 六 测试单独的功能
- 1 第一种
- 2 第二种
- 七 完整代码示例
- 八 源码地址
本文介绍了如何使用 Python 的原生测试框架 Unittest 进行单元测试,从基础的测试编写到复杂的场景覆盖。首先,展示了如何在不使用测试框架的情况下手动调试代码,并对比了使用 Unittest 的好处。文章通过多个实际示例,介绍了如何编写测试用例、处理断言和异常、分离测试文件以及执行多功能测试。同时,探讨了 Unittest 提供的常用断言方法,并展示了如何单独测试某个功能。最后,总结了编写高效单元测试的建议,使开发者在项目中更好地保证代码质量。
一 什么是 Unittest
unittest 是 Python 原生的测试框架。
1 不使用 Unittest 测试框架
def my_div(a, b):
return a / b
# ZeroDivisionError: division by zero
my_div(1, 0)
程序抛出错误。不用测试框架手动运行调试,即边开发边调试,这种方式比较适合以下场景。
- 项目整体规模较小
- 项目功能数量较少
- 项目功能之间相对独立
2 使用 Unittest 测试框架
TestFunc 继承 unittest.TestCase ,使用 unittest.main() 调用单元测试就写好了。
以下是运行没问题的单元测试。
# TestFunc 继承 unittest.TestCase
class TestFunc(unittest.TestCase):
def test_div(self):
# 运行之后 OK
self.assertEqual(2, my_div(2, 1))
self.assertEqual(-2, my_div(2, -1))
if __name__ == '__main__':
print_hi('单元测试')
unittest.main()
# 运行之后
# Ran 1 test in 0.000s
# OK
以下是运行抛异常的单元测试。
# TestFunc 继承 unittest.TestCase
class TestFunc02(unittest.TestCase):
def test_div(self):
# 这里后面我填了一个 1 纯粹是为了占一个位置,2/0 != 1,你知道的,
# 后面我们再介绍更优雅的写法
# 运行之后 FAILED (errors=1)
self.assertEqual(1, my_div(2, 0))
if __name__ == '__main__':
print_hi('单元测试')
unittest.main()
# 运行之后 FAILED (errors=1)
二 unittest 使用建议
假如要开发一个:输入 1 返回 2,输入 - 1 返回 3 ,输入其他任何数,返回 1 的程序。
1 先写测试 case 后写测试逻辑
建议先写 unittest 当中的 case,再写你要封装的函数 my_func03。
def my_func03(a):
# 逻辑先空在这里
return None
class TestFunc03(unittest.TestCase):
def test_func(self):
self.assertEqual(2, my_func03(1))
self.assertEqual(3, my_func03(-1))
for i in range(-100, 100):
if i == 1 or i == -1:
continue
self.assertEqual(1, my_func03(i))
2 测试文件以 _test.py 结尾
建议一个 py 文件一个 test ,比如文件是 yourpython.py ,则单元测试可以命名为 yourpython_test.py
三 多个功能测试
一个类 TestFunc04 中定义多个测试函数。
def my_func1(a):
if a == 1:
return 2
elif a == -1:
return 3
else:
return 1
def my_func2(b):
if b != "yes":
raise ValueError("you can only say yes!")
else:
return True
class TestFunc04(unittest.TestCase):
def test_func1(self):
self.assertEqual(2, my_func1(1))
self.assertEqual(3, my_func1(-1))
for i in range(-100, 100):
if i == 1 or i == -1:
continue
self.assertEqual(1, my_func1(i))
def test_func2(self):
self.assertTrue(my_func2("yes"))
with self.assertRaises(ValueError):
my_func2("nononono")
四 用 Python 命令执行测试
$ python -m unittest 单元测试.py
五 断言 assert 常用方法
在 unittest 的模块中有很多丰富的测试方法函数。
assert 断言 | 含义 |
---|---|
assertEqual(a, b) | a == b |
assertNotEqual(a, b) | a != b |
assertTrue(condition) | condition 是不是 True |
assertFalse(condition) | condition 是不是 False |
assertGreater(a, b) | a > b |
assertGreaterThan(a, b) | a >= b |
assertLess(a, b) | a < b |
assertLessEqual(a, b) | a <= b |
assertIs(a, b) | a is b ,a 和 b 是不是同一对象 |
assertIsNot(a, b) | a is not b ,a 和 b 是不是不同对象 |
assertIsNone(a) | a is None ,a 是不是 None |
assertIsNotNone(a) | a is not None ,a 不是 None? |
assertIn(a, b) | a in b , a 在 b 里面? |
assertNotIn(a, b) | a not in b ,a 不在 b 里? |
assertRaises(err) | 通常和 with 一起用,判断 with 里的功能是否会报错(上面练习有用到过) |
六 测试单独的功能
测试类 TestFunc05 中的 test_func1 方法。常用的两种方式。
1 第一种
使用 TestSuite() 和 TextTestRunner()
# 第一种,使用 TestSuite() 和 TextTestRunner()
class TestFunc05(unittest.TestCase):
def test_func1(self):
self.assertEqual(2, my_func1(1))
self.assertEqual(3, my_func1(-1))
for i in range(-100, 100):
if i == 1 or i == -1:
continue
self.assertEqual(1, my_func1(i))
def test_func2(self):
self.assertTrue(my_func2("yes"))
with self.assertRaises(ValueError):
my_func2("nononono")
# 定义一个 suite 替换 unittest.main()
suite = unittest.TestSuite()
suite.addTest(TestFunc04('test_func1'))
unittest.TextTestRunner().run(suite)
2 第二种
# 第二种,Python 的命令来执行不同的 test
$ python -m unittest 单元测试.TestFunc05.test_func1
$ python -m unittest 单元测试.TestFunc05.test_func2
七 完整代码示例
# This is a sample Python script.
import unittest
# Press ⌃R to execute it or replace it with your code.
# Press Double ⇧ to search everywhere for classes, files, tool windows, actions, and settings.
def print_hi(name):
# Use a breakpoint in the code line below to debug your script.
print(f'Hi, {name}') # Press ⌘F8 to toggle the breakpoint.
# 什么是 Unittest
# 不用 Unittest 单元测试,可能是先运行试试
# ZeroDivisionError: division by zero
# 这种方式比较适合
# 小型项目,
# 没有多少个功能的项目,
# 而且项目功能之间并不会有任何联系。
# my_div(1, 0)
def my_div(a, b):
return a / b
# 使用 unittest
# TestFunc 继承 unittest.TestCase
# class TestFunc(unittest.TestCase):
# def test_div(self):
# # 运行之后 OK
# self.assertEqual(2, my_div(2, 1))
# self.assertEqual(-2, my_div(2, -1))
# class TestFunc02(unittest.TestCase):
# def test_div(self):
# # 这里后面我填了一个 1 纯粹是为了占一个位置,2/0 != 1,你知道的,
# # 后面我们再介绍更优雅的写法
# # 运行之后 FAILED (errors=1)
# self.assertEqual(1, my_div(2, 0))
unittest.main()
# unittest 规范
# 假如要开发一个:输入 1 返回 2,输入 - 1 返回 3 ,输入其他任何数,返回 1 的程序
# 建议先写 unittest 当中的 case,再写你要封装的函数 my_func03
def my_func03(a):
# 逻辑先空在这里
return None
# class TestFunc03(unittest.TestCase):
# def test_func(self):
# self.assertEqual(2, my_func03(1))
# self.assertEqual(3, my_func03(-1))
# for i in range(-100, 100):
# if i == 1 or i == -1:
# continue
# self.assertEqual(1, my_func03(i))
# 建议一个 py 文件一个 test ,比如文件是 yourpython.py ,则单元测试可以命名为 yourpython_test.py
# 有时候要测试多个功能
def my_func1(a):
if a == 1:
return 2
elif a == -1:
return 3
else:
return 1
def my_func2(b):
if b != "yes":
raise ValueError("you can only say yes!")
else:
return True
class TestFunc04(unittest.TestCase):
def test_func1(self):
self.assertEqual(2, my_func1(1))
self.assertEqual(3, my_func1(-1))
for i in range(-100, 100):
if i == 1 or i == -1:
continue
self.assertEqual(1, my_func1(i))
def test_func2(self):
self.assertTrue(my_func2("yes"))
with self.assertRaises(ValueError):
my_func2("nononono")
# 用 Python 命令执行测试
# python -m unittest 单元测试.py
# 能测哪些 assert,在 unittest 的模块中有特别丰富的测试方式,常用的方法
# 。。。
# 想测单独的功能
# 第一种,使用 TestSuite() 和 TextTestRunner()
class TestFunc05(unittest.TestCase):
def test_func1(self):
self.assertEqual(2, my_func1(1))
self.assertEqual(3, my_func1(-1))
for i in range(-100, 100):
if i == 1 or i == -1:
continue
self.assertEqual(1, my_func1(i))
def test_func2(self):
self.assertTrue(my_func2("yes"))
with self.assertRaises(ValueError):
my_func2("nononono")
# 定义一个 suite 替换 unittest.main()
suite = unittest.TestSuite()
suite.addTest(TestFunc04('test_func1'))
unittest.TextTestRunner().run(suite)
# 第二种,Python 的命令来执行不同的 test
# python -m unittest 单元测试.TestFunc05.test_func2
# python -m unittest 单元测试.TestFunc05.test_func1
# Press the green button in the gutter to run the script.
if __name__ == '__main__':
print_hi('单元测试')
# See PyCharm help at https://www.jetbrains.com/help/pycharm/
复制粘贴并覆盖到你的 main.py 中运行,运行结果如下。
Testing started at 16:02 ...
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Ran 4 tests in 0.001s
OK
八 源码地址
代码地址:
国内看 Gitee 之 单元测试.py
国外看 GitHub 之 单元测试.py
引用 莫烦 Python