unittest 单元测试

软件系统的开发是一个很复杂的过程,随着系统复杂性的提高,代码中隐藏的 bug 也可能变得越来越多。为了保证软件的质量,测试是一个必不可少的部分,甚至还有测试驱动开发(Test-driven development, TDD)的理念,也就是先测试再编码。

在计算机编程中,单元测试(Unit Testing)又称为模块测试,是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作,所谓的单元是指一个函数,一个模块,一个类等。

在 Python 中,我们可以使用 unittest 模块编写单元测试。

下面以官方文档的例子进行介绍,该例子对字符串的一些方法进行测试:

# -*- coding: utf-8 -*- import <span class="wp_keywordlink_affiliate"><a href="https://www.168seo.cn/tag/unittest" title="View all posts in unittest" target="_blank">unittest</a></span> class TestStringMethods(<span class="wp_keywordlink_affiliate"><a href="https://www.168seo.cn/tag/unittest" title="View all posts in unittest" target="_blank">unittest</a></span>.TestCase): def test_upper(self): self.assertEqual('foo'.upper(), 'FOO') # 判断两个值是否相等 def test_isupper(self): self.assertTrue('FOO'.isupper()) # 判断值是否为 True self.assertFalse('Foo'.isupper()) # 判断值是否为 False def test_split(self): s = 'hello world' self.assertEqual(s.split(), ['hello', 'world']) # check that s.split fails when the separator is not a string with self.assertRaises(TypeError): # 检测异常 s.split(2) 
   
    

           1          

           2          

           3          

           4          

           5          

           6          

           7          

           8          

           9          

           10          

           11          

           12          

           13          

           14          

           15          

           16          

           17          

           18          

           19          

           20          


           # -*- coding: utf-8 -*-          

                      

           import                       unittest          

                      

           class                       TestStringMethods           (           unittest           .           TestCase           )           :          

                      

                          def                       test_upper           (           self           )           :          

                              self           .           assertEqual           (           'foo'           .           upper           (           )           ,                       'FOO'           )                          # 判断两个值是否相等          

                      

                          def                       test_isupper           (           self           )           :          

                              self           .           assertTrue           (           'FOO'           .           isupper           (           )           )                                # 判断值是否为 True          

                              self           .           assertFalse           (           'Foo'           .           isupper           (           )           )                               # 判断值是否为 False          

                      

                          def                       test_split           (           self           )           :          

                              s                       =                       'hello world'          

                              self           .           assertEqual           (           s           .           split           (           )           ,                       [           'hello'           ,                       'world'           ]           )          

                              # check that s.split fails when the separator is not a string          

                              with                       self           .           assertRaises           (           TypeError           )           :                               # 检测异常          

                                  s           .           split           (           2           )          

                      

   
在上面,我们定义了一个 TestStringMethods 类,它从 unittest.TestCase 继承。注意到,我们的方法名都是以 test 开头,表明该方法是测试方法,不以 test 开头的方法测试的时候不会被执行。
在方法里面,我们使用了断言(assert)判断程序运行的结果是否和预期相符。其中:
• 
assertEqual 用于判断两个值是否相等;
• 
assertTrue/assertFalse 用于判断表达式的值是 True 还是 False;
• 
assertRaises 用于检测异常;
断言方法主要有三种类型:
• 检测两个值的大小关系:相等,大于,小于等
• 检查逻辑表达式的值:True/Flase
• 检查异常

 下面列举了部分常用的断言方法:
Method
Checks that
assertEqual(a, b)
a == b
assertNotEqual(a, b)
a != b
assertGreater(a, b)
a > b
assertGreaterEqual(a, b)
a >= b
assertLess(a, b)
a < b
assertLessEqual(a, b)
a <= b
assertTrue(x)
bool(x) is True
assertFalse(x)
bool(x) is False
assertIs(a, b)
a is b
assertIsNot(a, b)
a is not b
assertIsNone(x)
x is None
assertIsNotNone(x)
x is not None
assertIn(a, b)
a in b
assertNotIn(a, b)
a not in b
assertIsInstance(a, b)
isinstance(a, b)
assertNotIsInstance(a, b)
not isinstance(a, b)
现在,让我们来运行上面的单元测试,将上面的代码保存为文件 mytest.py,通过 -m unittest 参数运行单元测试:
                                         

     $ <span class="wp_keywordlink"><a href="http://www.168seo.cn/python" title="python">python</a></span> -m unittest mytest test_isupper (mytest.TestStringMethods) ... ok test_split (mytest.TestStringMethods) ... ok test_upper (mytest.TestStringMethods) ... ok 
   
    

           1          

           2          

           3          

           4          

           5          


           $                       python                       -           m                       unittest                       mytest          

           test_isupper                       (           mytest           .           TestStringMethods           )                       .           .           .                       ok          

           test_split                       (           mytest           .           TestStringMethods           )                       .           .           .                       ok          

           test_upper                       (           mytest           .           TestStringMethods           )                       .           .           .                       ok          

                      

   
执行结果:
                                         

     ... ---------------------------------------------------------------------- Ran 3 tests in 0.000s OK 
   
    

           1          

           2          

           3          

           4          

           5          

           6          


           .           .           .          

           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --          

           Ran                       3                       tests            in                       0.000s          

                      

           OK          

                      

   
上面的结果表明测试通过,我们也可以加 -v 参数得到更加详细的测试结果:
                                         

     $ python -m unittest -v mytest test_isupper (mytest.TestStringMethods) ... ok test_split (mytest.TestStringMethods) ... ok test_upper (mytest.TestStringMethods) ... ok ---------------------------------------------------------------------- Ran 3 tests in 0.000s OK 
   
    

           1          

           2          

           3          

           4          

           5          

           6          

           7          

           8          

           9          

           10          


           $                       python                       -           m                       unittest                       -           v                       mytest          

           test_isupper                       (           mytest           .           TestStringMethods           )                       .           .           .                       ok          

           test_split                       (           mytest           .           TestStringMethods           )                       .           .           .                       ok          

           test_upper                       (           mytest           .           TestStringMethods           )                       .           .           .                       ok          

                      

           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --          

           Ran                       3                       tests            in                       0.000s          

                      

           OK          

                      

   
上面这种运行单元测试的方法是我们推荐的做法,当然,你也可以在代码的最后添加两行:
                                         

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

           1          

           2          

           3          


           if                       __name__                       ==                       '__main__'           :          

                          unittest           .           main           (           )          

                      

   
然后再直接运行:
                                         

     $ python mytest.py 
   
    

           1          

           2          


           $                       python            mytest           .           py          

                      

   
setUp 和 tearDown
在某些情况下,我们需要在每个测试方法执行前和执行后做一些相同的操作,比如我们想在每个测试方法执行前连接数据库,执行后断开数据库连接,为了避免在每个测试方法中编写同样的代码,我们可以使用 setUp 和 tearDown 方法,比如:
                                         

     # -*- coding: utf-8 -*- import unittest class TestStringMethods(unittest.TestCase): def setUp(self): # 在每个测试方法执行前被调用 print 'setUp, Hello' def tearDown(self): # 在每个测试方法执行后被调用 print 'tearDown, Bye!' def test_upper(self): self.assertEqual('foo'.upper(), 'FOO') # 判断两个值是否相等 def test_isupper(self): self.assertTrue('FOO'.isupper()) # 判断值是否为 True self.assertFalse('Foo'.isupper()) # 判断值是否为 False def test_split(self): s = 'hello world' self.assertEqual(s.split(), ['hello', 'world']) # check that s.split fails when the separator is not a string with self.assertRaises(TypeError): # 检测异常 s.split(2) 
   
    

           1          

           2          

           3          

           4          

           5          

           6          

           7          

           8          

           9          

           10          

           11          

           12          

           13          

           14          

           15          

           16          

           17          

           18          

           19          

           20          

           21          

           22          

           23          

           24          

           25          

           26          


           # -*- coding: utf-8 -*-          

                      

           import                       unittest          

                      

           class                       TestStringMethods           (           unittest           .           TestCase           )           :          

                      

                          def                       setUp           (           self           )           :                                                    # 在每个测试方法执行前被调用          

                              print                       'setUp, Hello'          

                      

                          def                       tearDown           (           self           )           :                                                 # 在每个测试方法执行后被调用          

                              print                       'tearDown, Bye!'          

                      

                          def                       test_upper           (           self           )           :          

                              self           .           assertEqual           (           'foo'           .           upper           (           )           ,                       'FOO'           )                          # 判断两个值是否相等          

                      

                          def                       test_isupper           (           self           )           :          

                              self           .           assertTrue           (           'FOO'           .           isupper           (           )           )                                # 判断值是否为 True          

                              self           .           assertFalse           (           'Foo'           .           isupper           (           )           )                               # 判断值是否为 False          

                      

                          def                       test_split           (           self           )           :          

                              s                       =                       'hello world'          

                              self           .           assertEqual           (           s           .           split           (           )           ,                       [           'hello'           ,                       'world'           ]           )          

                              # check that s.split fails when the separator is not a string          

                              with                       self           .           assertRaises           (           TypeError           )           :                               # 检测异常          

                                  s           .           split           (           2           )          

                      

   
看看执行结果:
                                         

     $ python -m unittest -v mytest test_isupper (mytest.TestStringMethods) ... setUp, Hello tearDown, Bye! ok test_split (mytest.TestStringMethods) ... setUp, Hello tearDown, Bye! ok test_upper (mytest.TestStringMethods) ... setUp, Hello tearDown, Bye! ok ---------------------------------------------------------------------- Ran 3 tests in 0.000s OK 
   
    

           1          

           2          

           3          

           4          

           5          

           6          

           7          

           8          

           9          

           10          

           11          

           12          

           13          

           14          

           15          

           16          


           $                       python                       -           m                       unittest                       -           v                       mytest          

           test_isupper                       (           mytest           .           TestStringMethods           )                       .           .           .                       setUp           ,                       Hello          

           tearDown           ,                       Bye           !          

           ok          

           test_split                       (           mytest           .           TestStringMethods           )                       .           .           .                       setUp           ,                       Hello          

           tearDown           ,                       Bye           !          

           ok          

           test_upper                       (           mytest           .           TestStringMethods           )                       .           .           .                       setUp           ,                       Hello          

           tearDown           ,                       Bye           !          

           ok          

                      

           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --           --          

           Ran                       3                       tests            in                       0.000s          

                      

           OK

小结

  • 通过从 unittest.TestCase 继承来编写测试类。
  • 使用断言方法判断程序运行的结果是否和预期相符。
  • setUp 在每个测试方法执行前被调用,tearDown 在每个测试方法执行后被调用。