Python-unittest单元测试

单元测试

单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。

比如对函数abs(),可以编写出以下几个测试用例:

  1. 输入整数时,如11.20.99,期待返回值与输入相同。
  2. 输入负数时,如-1-1.2-0.99,期待返回值与输入相反。
  3. 输入0时,期待返回值为0.
  4. 输入非数值类型,如None[]{},期待抛出TypeError异常。

把上面的测试用例放到一个测试模块里,就是一个完整的单元测试。

如果单元测试通过,说明我们测试的这个函数能够正常工作。如果单元测试不通过,要么函数有bug,要么测试条件输入不正确,总之,需要修复使单元测试能够通过。

当我们修改了一个函数的代码,但是不想让它原有的功能改变,当我们修改后,只需要再跑一遍单元测试。如果通过,说明我们的修改没有影响原有的功能。

Python中内置了一个单元测试的框架unittest,下面我们通过一个单元测试来执行以下,abs()的测试用例。

unittest

继承 unittest.TestCase 就创建了一个测试样例。

测试脚本abs_test.py中的代码如下:

import unittest

class TestAbsFunc(unittest.TestCase):
    def test_positive(self):
        self.assertEqual(abs(1), 1)
        self.assertEqual(abs(1.2), 1.2)
        self.assertEqual(abs(0.99), 0.99)

    def test_negative(self):
        self.assertEqual(abs(-1), 1)
        self.assertEqual(abs(-1.2), 1.2)
        self.assertEqual(abs(-0.99), 0.99)
        
    def test_zero(self):
        self.assertEqual(abs(0), 0)
        
    def test_nonumber(self):
        with self.assertRaises(TypeError):
            abs(None)
        with self.assertRaises(TypeError):
            abs([])
        with self.assertRaises(TypeError):
            abs({})


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

上面代码执行的结果为:

image-20211015163706963

从运行结果可以看出:进行了4个测试,测试结果为:OK

下面来分析一下代码:

首先我们创建了一个TestAbsFunc类,该类继承自unittest.TestCase。此时这个类就是一个测试类。

这个类中包含了4个方法,可以看出全都是test开头的,因为test开头的方法被认为是测试方法,执行测试的时候只会执行测试方法,其中有两个方法是例外setUptearDown,后面会介绍它们的作用

四个以test开头的方法分别对应四种测试用例。由于unittest.TestCase提供了很多内置的条件判断,我们只需要调用这些方法就可以断言输出是否是我们所期望的。最常用的断言就是assertEqual()

self.assertEqual(abs(1), 1) # 断言函数的返回值是否与1相等

另一种重要的断言就是期待抛出指定类型Error,比如通过abs(None) 来返回None的绝对值,会抛出一个TypeError

with self.assertRaises(TypeError):
    abs(None)

运行单元测试

一旦编写好单元测试,我们就可以运行单元测试。最简单的运行方式是在测试类的最后加上两行代码:

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

这样就可以把abs_test.py当做正常的python脚本运行:

$ python mydict_test.py

另一种方法是在命令行通过参数-m unittest直接运行单元测试:

$ python -m unittest abs_test

image-20211015164320135

setUp 和 tearDown

可以在单元测试中编写两个特殊的setUp()tearDown()方法。这两个方法会分别在每调用一个测试方法的前后分别被执行。

setUp()tearDown()方法有什么用呢?设想你的测试需要启动一个数据库,这时,就可以在setUp()方法中连接数据库,在tearDown()方法中关闭数据库,这样,不必在每个测试方法中重复相同的代码,在TestAbsFunc 中添加这两个方法:

def setUp(self):
    print('setUp...')

def tearDown(self):
	print('tearDown...')

image-20211015165024983

unittest命令选项

-b, --buffer

在测试运行时,标准输出流与标准错误流会被放入缓冲区。成功的测试的运行时输出会被丢弃;测试不通过时,测试运行中的输出会正常显示,错误会被加入到测试失败信息。

-f, --failfast

当出现第一个错误或者失败时,停止运行测试。

-v

获取更详细(更多的冗余)的信息。

posted @ 2021-10-15 16:58  雨-铃  阅读(143)  评论(0编辑  收藏  举报
Tips
复制成功!