unittest 单元测试 教程

unittest 单元测试

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

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

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

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

Python
# -*- 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)
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 参数运行单元测试:

Python
$ python -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
 

执行结果:

Python
... ---------------------------------------------------------------------- Ran 3 tests in 0.000s OK
1
2
3
4
5
6
. . .
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
Ran 3 tests in 0.000s
 
OK
 

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

Python
$ 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
 

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

Python
if __name__ == '__main__': unittest.main()
1
2
3
if __name__ == '__main__' :
     unittest . main ( )
 

然后再直接运行:

Python
$ python mytest.py
1
2
$ python mytest . py
 

setUp 和 tearDown

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

Python
# -*- 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
$ 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 在每个测试方法执行后被调用。



你可能感兴趣的:(unittest 单元测试 教程)