进入正文
python标准库系列教程(五)——unittest库单元测试(上篇)
声明:前面的python标准库系列文章详细讲解了Python的三大函数式编程库以及集合库,itertools、functools、operators库以及collections库,本文继续python标准库系列文章,本文为第五篇,深入详解python的单元测试原理以及unittest的基本架构,鉴于篇幅较长,第五篇将分为几个小的篇章说明,本文为上篇,后续还会有系列文章更新,希望对小伙伴有所帮助!
目录
一 pytho单元测试的基本概念
1.1 什么是“单元测试”
1.2 什么是“unittest”
1.3 unittest的基本架构
1.4 unittest的基本流程
二 unittest的简单实例——TestCase
2.1 使用unittest实现一个最基础的单
元测试
2.2 单元测试的详细信息输出
三 测试函数的组织——TestSuite
3.1 使用TestSuite进行Case的顺序组
织
3.2 使用TestLoader将要测试的Case
添加到TestSuite里面
3.3 两个最核心的方法讲解
四 unittest单元测试总结
4.1 单元测试的一般流程(三步走)
4.2 编写单元测试方法(以test开头)的
常用断言方法Assertion
五 下一篇预告
01
pytho单元测试的基本概念
什么是“单元测试”
什么是“unittest”
unittest的基本架构
unittest的基本流程
通过unittest类调用分析,可将框架的工作流程概况如下:
编写TestCase,由TestLoader加载TestCase到TestSuite,然后由TextTestRunner来运行TestSuite, 最后将运行的结果保存在TextTestResult中
02
unittest的简单实例——TestCase
使用unittest实现一个最基础的单元测试
比如我有一个模块functions,里面定义了4个函数分别实现加减乘除,现在我要测试这四个函数的正确与否。
def add(a, b):
return a+b
def minus(a, b):
return a-b
def multi(a, b):
return a*b
def divide(a, b):
return a/b
现在我要测试上面的四个方法,我们可以这样做,新建一个Python文件test.py,代码如下:
from functions import *
import unittest
class TestFunctions(unittest.TestCase):
def test_add(self): #测试加法的方法
self.assertEqual(3, add(1, 2))
self.assertNotEqual(3, add(2, 2))
def test_minus(self): #测试减法的方法
self.assertEqual(1, minus(3, 2))
def test_multi(self): #测试乘法的方法
self.assertEqual(6, multi(2, 3))
def test_divide(self): #测试除法的方法
self.assertEqual(2, divide(6, 3))
self.assertEqual(2.5, divide(5, 2))
if __name__ == '__main__':
unittest.main()
然后运行上面的test.py这个模块,即单元测试的一般步骤总结:第一步:选定需要测试的函数。比如上面的加减乘除四个方法;第二步:定义一个测试类(核心)。继承自unittest.TestCase类,然后在类中定义实例测试方法,这里需要注意的是,测试方法一定要以“test”开头,表示这是一个测试方法,否则是不会进行运行的,当然为了习惯性地表示,和便于查看,一般就写成“test_被测试方法名”这种形式了,但是后面的下划线和被测试方法名称不是强制性的,只要保证是test开头的即可,否则是没有办法被unittest识别的哦。第三步:通过unittest.main()来运行测试类。这里我们发现,没有构造自定义测试类的对象,更没有显示调用某个方法,但他就是运行了哦。main函数有一个verbosity 参数,该参数可以控制输出的错误报告的详细程度,默认是 1,如果设为 0,则不输出每一用例的执行结果,即没有上面的结果中的第1行(那几个点);如果设为 2,则输出详细的执行结果。注意:上面的运行结果中,有四个点.... 那么是什么意思呢?成功是 .,失败是 F,出错是 E,跳过是 S
单元测试的详细信息输出
上面的运行结果实在是太简单了,就几个点,我啥也看不明白,我们可以给每一个测试函数添加函数注释,并且在运行结果中显示出来,如下:
from functions import *
import unittest
class TestFunctions(unittest.TestCase):
"""这是其自己编写测试类"""
def test_add(self): #测试加法的方法
"""这是测试add的测试方法"""
self.assertEqual(3, add(1, 2))
self.assertNotEqual(3, add(2, 2))
def test_minus(self): #测试减法的方法
"""这是测试minus的测试方法"""
self.assertEqual(1, minus(3, 2))
def test_multi(self): #测试乘法的方法
"""这是测试multi的测试方法"""
self.assertEqual(6, multi(2, 3))
def test_divide(self): #测试除法的方法
"""这是测试divide的测试方法"""
self.assertEqual(2, divide(6, 3))
self.assertEqual(2.5, divide(5, 2))
if __name__ == '__main__':
unittest.main(verbosity=2)
运行结果为:
test_add (__main__.TestFunctions)
这是测试add的测试方法 ... ok
test_divide (__main__.TestFunctions)
这是测试divide的测试方法 ... ok
test_minus (__main__.TestFunctions)
这是测试minus的测试方法 ... ok
test_multi (__main__.TestFunctions)
这是测试multi的测试方法 ... ok
----------------------------------------------------------------------
Ran 4 tests in 0.005s
OK
从上面可以看出,测试函数中的注释内容也打印了出来,即合理的使用注释和描述,能够使输出的测试报告更加便于阅读。
03
测试函数的组织——TestSuite
概念声明:在上面的例子中,自定义的TestFunctions是一个测试类,里面的每一个测试方法,即以test开头的方法,称之为一个Case。我们添加到TestSuite中的Case是会按照添加的顺序执行的。其实顾名思义,Suite的英文含义就是“套件、套装”,即由某一种规则或者是约束的东西。(注意:我们每一个要测试的方法称之为一个Case)解决两类问题的:
使用TestSuite进行Case的顺序组织
import unittest
from test import TestFunctions #导入我自己定义的测试类
if __name__ == '__main__':
# 第一步:构建需要测试的测试方法列表,按照列表中的顺序进行测试,不在列表中的则不进行测试
tests = [TestFunctions("test_add"), TestFunctions("test_minus"), TestFunctions("test_divide")]
# 第二步:构建TestSuite对象,并将Case列表添加进去
suite = unittest.TestSuite()
suite.addTests(tests)
#第三步:构建一个TextTestRunner对象,并且运行第二步中的suite对象
runner = unittest.TextTestRunner(verbosity=2)
runner.run(suite)
运行结果为:
test_add (test.TestFunctions)
这是测试add的测试方法 ... ok
test_minus (test.TestFunctions)
这是测试minus的测试方法 ... ok
test_divide (test.TestFunctions)
这是测试divide的测试方法 ... ok
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
从上面的执行结果可以看出来,suite里面的三个Case是按照顺序进行测试的,且没有添加进来的就不会测试。注意:上面是先将Case组织成一个列表,然后一次性添加进Suite,我也可以以一个一个添加的,即:总结:三步走第一步:构建需要测试的测试方法列表,按照列表中的顺序进行测试,不在列表中的则不进行测试第二步:构建TestSuite对象,并将Case列表添加进去第三步:构建一个TextTestRunner对象,并且运行第二步中的suite对象
使用TestLoader将要测试的Case
添加到TestSuite里面
上面的例子,不管是一次性将所有的Case全部添加进Suite,还是一个一个添加,都是有序的,除此之外,我还可以使用TestLoader对象,这个也是unittest的5大核心构件之一,特也可以将Case加载到Suite里面去,代码如下:
import unittest
from test import TestFunctions #导入我自己定义的测试类
# 第一步:构造一个TestSuite对象
suite = unittest.TestSuite()
#第二步:构造一个TestLoader对象
loader=unittest.TestLoader()
#第三步:通过loader将所有的Case传递到Suite里面,故而没有先后顺序
suite.addTests(loader.loadTestsFromName('test.TestFunctions'))
#第四步:构建一个TextTestRunner对象,并且运行第二步中的suite对象
runner = unittest.TextTestRunner(verbosity=2)
runner.run(suite)
运行结果为:
test_add (test.TestFunctions)
这是测试add的测试方法 ... ok
test_divide (test.TestFunctions)
这是测试divide的测试方法 ... ok
test_minus (test.TestFunctions)
这是测试minus的测试方法 ... ok
test_multi (test.TestFunctions)
这是测试multi的测试方法 ... ok
----------------------------------------------------------------------
Ran 4 tests in 0.000s
OK
从上面可知,使用TestLoader加载的依然是没有顺序的,为什么呢?因为他不是按照顺序把每一个Case添加进suite,而是一次性将一个或者是好几个TestCase中的全部Case全部添加进去,至于顺序到底是怎样的,没办法确定。总结:四步走第一步:构造一个TestSuite对象第二步:构造一个TestLoader对象第三步:通过loader将TestFunctions里面所有的Case传递到Suite里面,故而是没有顺序的第四步:构建一个TextTestRunner对象,并且运行第二步中的suite对象
两个最核心的方法讲解
上面那么多的这步骤,这哪里记得住啊,其实完全没有必要死记硬背,只需要掌握两组核心方法就可以了suite.addTest(TestFunctions("test_multi")) ,即suite.addTest(自定义测试类("以test开头的测试方法名")) suite.addTests(tests) #添加多个Casesuite.addTests(loader.loadTestFrom***()),即它的参数也可以是loader的方法所返回的一个suite对象。
loader.loadTestsFromModul(module) #参数为一个模块名,如本文中的test。它返回一个模块中所包含的所有的Cases组成的suiteloader.loadTestsFromName('test.TestFunctions')) #参数为一个测试类名,但是要写成字符串的形式。它返回给定的测试类(如TestFunctions)中的所有的Cases所组成的suite。loader.loadTestsFromNames(['test.TestFunctions']) #参数为一个列表,列表的每一个元素都是由测试类名所组成的字符串。它返回一系列的测试类中所包含的所有的Cases所组成的suite.(这就解决多个测试类的问题,机上面所说的问题二)loader.loadTestsFromTestCase(TestFunctions) #参数为一个测试类,类中的所有的Cases所组成的suite。
04
unittest单元测试总结
单元测试的一般流程(三步走)
第一步:编写TestCase;
编写单元测试方法(以test开头)的
常用断言方法Assertion
unittest库提供了很多实用方法来检测程序运行的结果和预期。包括三种类型的方法,每一种都覆盖了典型的类型,比如:
Method | Checks that | New in |
assertEqual(a, b) | a == b | |
assertNotEqual (a, b) |
a != b | |
assertTrue(x) | bool(x) is True | |
assertFalse(x) | bool(x) is False | |
assertIs(a, b) | a is b | 3.1 |
assertIsNot(a, b) | a is not b | 3.1 |
assertIsNone(x) | x is None | 3.1 |
assertIsNotNone(x) | x is not None | 3.1 |
assertIn(a, b) | a in b | 3.1 |
assertNotIn(a, b) | a not in b | 3.1 |
assertIsInstance (a, b) |
isinstance(a, b) | 3.2 |
assertNotIsInstance (a, b) |
not isinstance (a, b) |
3.2 |
Method | Checks that | New in |
assertRaises (exc, fun, *args, **kwds) |
fun(*args, **kwds) raises exc |
|
assertRaises- Regex (exc, r, fun, *args, **kwds) |
fun(*args, **kwds) raises exc and t he message matches regex r |
3.1 |
assertWarns (warn, fun, *args, **kwds) |
fun(*args, **kwds) raises warn |
3.2 |
assertWarns- Regex (warn, r, fun, *args, **kwds) |
fun(*args, **kwds) raises warn and the message matches regex r |
3.2 |
assertLogs (logger, level) |
The with block logs on logger with minimum level |
3.4 |
Method | Checks that | New in |
assertAlmost- Equal(a, b) |
round(a-b, 7) == 0 |
|
assertNot- AlmostEqual(a, b) |
round(a-b, 7) != 0 |
|
assertGreater (a, b) |
a > b | 3.1 |
assertGreater- Equal(a, b) |
a >= b | 3.1 |
assertLess(a, b) | a < b | 3.1 |
assertLess- Equal(a, b) |
a <= b | 3.1 |
assertRegex(s, r) | r.search(s) | 3.1 |
assertNot- Regex(s, r) |
not r.search(s) | 3.2 |
assertCount- Equal(a, b) |
a and b have the same elements in the same number, regardless of their order |
3.2 |
Method | Used to compare | New in |
assertMulti-LineEqual (a, b) |
strings | 3.1 |
assertSequence- Equal(a, b) |
sequences | 3.1 |
assertList- Equal(a, b) |
lists | 3.1 |
assertTupleEqual (a, b) |
tuples | 3.1 |
assertSet- Equal(a, b) |
sets or frozensets | 3.1 |
assertDict- Equal(a, b) |
dicts | 3.1 |
Method Name | Deprecated alias | Deprecated alias |
|
assertEqual() | failUnlessEqual | assertEquals | |
assertNot Equal() |
failIfEqual | assertNot Equals |
|
assertTrue() | failUnless | assert_ | |
assertFalse() | failIf | ||
assertRaises() | failUnlessRaises | ||
assertAlmost Equal() |
failUnless AlmostEqual |
assertAlmost Equals |
|
assertNot AlmostEqual() |
failIfAlmost Equal |
assertNot AlmostEquals |
|
assertRegex() | assertRegexp Matches |
||
assertNot Regex() |
assertNot RegexpMatches |
||
assertRaises Regex() |
assertRaises Regexp |
05
下一篇预告
限于篇幅,下一篇会接着本篇内容详细讲解TestRunner、TestFixture、TextTestResult相关的内容,有兴趣可以继续关注。
2019/01/08
Tuesday
小伙伴们,单元测试是任何编程语言都不可避免的哦,看完这篇文章你一定会有不一样的收获的,后面还有系列文章连载,请记得关注哦!如果你有需要,就添加我的公众号哦,里面分享有海量资源,包含各类数据、教程等,后面会有更多面经、资料、数据集等各类干货等着大家哦,重要的是全都是免费、无套路分享,有兴趣的小伙伴请持续关注!
推 荐 阅 读
您的点赞和分享是我们进步的动力!
↘↘↘