单元测试
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 的时间,赋能你的大项目。