单元测试
Test! Test! Test! 重要的事说三遍。 不管是你自用的代码还是写给别人用的代码都需要测试。 尤其是你要写一些功能服务给别人用的时候,免不了要先自测有没有问题。 如果上一节内容我们学习的是怎样处理错误, 那这一节内容我们就是学习怎么样避免错误。
对于我来说,我通常处理的代码量比较大,牵扯到的资源相对多,所以我很多时候并不知道哪里会不会有错误。 所以我非常依赖于测试。让自动化测试帮我处理任何改动可能带来的问题。
什么是 Unittest¶
在很多语言中都有 Unittest,他是一种语言比较通用的测试功能。越流行,越好用的库,比如机器学习中的 Tensorflow, 它的 unittest 也就越多。原因也很简单,为了保证我写的,真的是我想要的这件事。
每一种语言的 Unittest 方法并不都一样。在 Python 中,我们常用一个原生的 unittest 做单元测试。
什么是单元测试呢?其实就是不直接测你的全套程序逻辑,而是将你的小功能模块拆开了一个个测。 这样做的目的非常明确,只有你能每一小步都做好,你的整体才不容易出错。 在 Python 中,如果你还没有接触过 unittest,你的测试流程很可能是运行整套逻辑,看看它会不会报错。
然后发现报错之后,在来看看是哪里报错了,接着就是修改报错的代码。这种方式比较适合
- 小型项目,
- 没有多少个功能的项目,
- 而且项目功能之间并不会有任何联系。
如果你不满足上面这几点,那要不试试 unittest? 在 Python 中,一个简单的 unittest 是下面这样。下面我们继承了 unittest.TestCase, 还不明白继承是啥意思的同学,请看到这里。
上面两个都没什么问题。但是当我们再测试另一个除以零的 case 的时候,它就会帮我报出问题啦。
unittest 规范¶
前面我们已经展示了一个特别小的案例。现在我们来说说写 unittest 的思路和规范吧。 首先 unittest 不会被其他人使用到,纯粹是你自己为了验证自己写的代码有没有问题的方式。 另外,你可以按照 unittest 当中的 case 为蓝本,去完善你原函数的功能。 就好像有了一个目标,你要为了这个目标去开发功能一样。
我举一个例子,我想要开发一个:
- 输入 1 返回 2
- 输入 -1 返回 3
- 输入其他任何数,返回 1
这样的一个功能,那我就可以先写 unittest 当中的 case,比如下面,我不会先写 my_func 里面的内容,而是先把我的规划写好, 要验收的指标写好。然后后面我在开发功能。
另外还有一个不成文的规范,我的这个 my_func() 通常是写在另一个 Python 文件中的,比如 all_my_func.py, 我在测试文件中,比如 test.py 是会将 all_my_func.py 的 my_func() 引入进来做测试的。 这样测试就不会和我原本的功能文件混杂在一起了。有点类似于下图的分工。
而且很多时候,你并不只有一个功能要测试,你还会有很多其他的,想一起测试。这也很好办。 而且如果你其中某一个有问题,他也会单独指出到底是哪一个有问题,问题在哪。 你多把玩一下,看看它的特性。
用 Python 命令执行测试¶
注意,有些人可能会比较喜欢通过 Python 的指令来运行测试,比如下面这样。test.py 就是我们写 test 的文件啦,如上面的测试都可以写在 test.py 中。
python -m unittest tests.py
能测哪些 assert¶
在 unittest 的模块中,我们还有特别丰富的测试方式,比如上面你看到的 self.assertTrue(),self.assertEqual()。我在下面再列一些比较常用的。
| 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 里的功能是否会报错(上面练习有用到过) |
测单独的功能¶
如果你写了很多 test,但是只想 test 某些功能,咋整?有复杂的办法,也有简单的。先说复杂的,你在你的 test.py 中,将代码最下边的 unittest.main() 替换成下面这段代码中那些 TestSuite() 和 TextTestRunner() 部分。但是这样写不太友善,因为你测试的变动挺多的, 一会儿想测这些,一会儿想测些别的,这样写非常不灵活。
我们还可以不用上面 unittest.TestSuite() 的写法,直接用 Python 的命令来执行不同的 test。 下面这种写法灵活性更强,我们也能实现上面的那些 suite 方法。
python -m unittest tests.TestFunc.test_func2
总结¶
测试!测试!测试!别忘了测试。测试真的很重要,很多时候还节约了你 debug 的时间,赋能你的大项目。
