Python单元测试实例详解


Posted in Python onMay 25, 2018

本文实例讲述了Python单元测试。分享给大家供大家参考,具体如下:

在Python中进行单元测试需要用到自动单元测试框架PyUnit,Python2.1及其以后的版本都将PyUnit作为一个标准模块(即python的unittest模块),如果你很out,那么你需要从PyUnit网站下载源码安装后才能使用。

一、Python单元测试范例

测试最基本的原理是比较预期结果是否与实际执行结果相同,如果相同则测试成功,否则测试失败。为了更好地理解自动测试框架PyUnit,下面会以对Widget类进行测试为例说明之:

#widget.py
#将要被测试的类Widget
class Widget:
  def __init__(self, size = (40, 40)):
    self._size = size
  def getSize(self):
    return self._size
  def resize(self, width, height):
    if width < 0 or height < 0:
      raise ValueError, "illegal size"
    self._size = (width, height)
  def dispose(self):
    pass

二、测试用例TestCase

软件测试中最基本的组成单元式测试用例(test case),PyUnit使用TestCase类来表示测试用例,并要求所有用于执行测试的类都必须从该类继承。TestCase子类实现的测试代码应该是自包含的(self contained),即测试用例既可以单独运行,也可以和其它测试用例构成集合共同运行。TestCase类中常用的函数或方法有:

setUp:进行测试前的初始化工作。
tearDown:执行测试后的清除工作。
failedinfo:表示不成立打印信息faliedinfo,为可选参数。
self.assertEqual(value1, value2, failedinfo):会无条件的导致测试失败,不推荐使用。
self.assertTrue(, failedinfo):断言value1 == value2。
self.assertFalse(, failedinfo):断言value为真。
self.assertRaises(ValueError, self.widget.resize, -1, -1):断言肯定发生异常,如果没发生异常,则为测试失败。参数1为异常,参数2为抛出异常的调用对象,其余参数为传递给可调用对象的参数。
TestCase在PyUnit测试框架中被视为测试单元的运行实体,Python程序员可以通过它派生自定义的测试过程与方法(测试单元),利用Command和Composite设计模式,多个TestCase还可以组合成测试用例集合。PyUnit测试框架在运行一个测试用例时,TestCase子类定义的setUp()runTest()tearDown()方法被依次执行,最简单的测试用例只需要覆盖runTest()方法来执行特定的测试代码就可以了。

1、静态方法

一个测试用例只对软件模块中一个方法进行测试,采用覆盖runTest()方法来构造测试用例,这在PyUnit中称之为静态方法,举例说明如下:

#static.py
from widget import Widget
import unittest
#执行测试的类
class WidgetTestCase(unittest.TestCase):
  def runTest(self):
    widget = Widget()
    self.assertEqual(widget.getSize(), (40, 40))
#测试
if __name__ == "__main__":
  testCase = WidgetTestCase()
  testCase.runTest()

如果采用静态方法,Python程序员就不得不为每个要测试的方法编写一个测试类,该类通过覆盖runTest()方法来执行测试,并在每个测试类中生成一个待测试的对象,这样会非常繁琐与笨拙。

2、动态方法

鉴于静态方法的缺陷,PyUnit提供了另一种高帅富的解决方法,即动态方法,只编写一个测试类来完成对整个软件模块的测试,这样对象的初始化工作可以在setUp()方法中完成,而资源的释放则可以在tearDown()方法中完成,举例说明如下:

#dynamic.py
from widget import Widget
import unittest
class WidgetTestCase(unittest.TestCase):
  def setUp(self):
    self.widget = Widget()
  def tearDown(self):
    self.widget.dispose()
    self.widget = None
  def testSize(self):
    self.assertEqual(self.widget.getSize(), (40, 40))
  def testResize(self):
    self.widget.resize(100, 100)
    self.assertEqual(self.widget.getSize(), (100, 100))

动态方法不再覆盖runTest()方法,而是为测试类编写多个测试方法,按照惯例这些方法通常以test开头但这不是必须的,在创建TestCase子类的实例时必须给出测试方法的名称来为PyUnit测试框架指明运行该测试用例时应该调用测试类中的哪些方法,这通常会结合测试用例集TestSuite一起使用。

三、测试用例集TestSuite

完整的单元测试很少只执行一个测试用例,开发人员通常需要编写多个测试用例才能对某一软件功能进行比较完成的测试,这些相关的测试用例称为一个测试用例集,在PyUnit中是用TestSuite类来表示的。PyUinit测试框架允许Python程序员在单元测试代码中定义一个名为suite()的全局函数,并将其作为整个单元测试的入口,PyUnit通过调用它来完成整个测试过程:

def suite():
  suite = unittest.TestSuite()
  suite.addTest(WidgetTestCase("testSize"))
  suite.addTest(WidgetTestCase("testResize"))
  return suite
    也可以直接定义一个TestSuite的子类,并在其初始化方法__init__中完成所有测试用例的添加:
class WidgetTestSuite(unittest.TestSuite)
  def __init__(self):
    unittest.TestSuite.__init__(self, map(WidgetTestCase, ("testSize", "testResize")))
    这样只需要在suite()方法中返回该类的一个实例就可以了:
def suite():
  return WidgetTestSuite()

在PyUnit测试框架中,TestSuite类可以看成是TestCase类的一个容器,用来对多个测试用例进行组织,这样多个测试用例可以自动在一次测试中全部完成。事实上,TestSuite除了可以包含TestCase外,也可以包含TestSuite,从而可以构成一个更庞大的测试用例集:

suite1 = mysuite1.TheTestSuite()
suite2 = mysuite2.TheTestSuite()
alltests = unittest.TestSuite((suite1, suite2))

四、实施测试TestRunner

编写测试用例(TestCase)并将它们组织成测试用例集(TestSuite)的最终目的只有一个:实施测试并获得最终结果。PyUnit使用TestRunner类作为测试用例的基本执行环境,来驱动整个单元测试过程。但是Python开发人员在进行单元测试时一般不直接使用TestRunner类,而是使用其子类TextTestRunner来完成测试,并将测试结果以文本方式显示出来。举例说明如下:

#text_runner.py
from widget import Widget
import unittest
#执行测试的类
class WidgetTestCase(unittest.TestCase):
  def setUp(self):
    self.widget = Widget()
  def tearDown(self):
    self.widget.dispose()
    self.widget = None
  def testSize(self):
    self.assertEqual(self.widget.getSize(), (40, 40))
  def testResize(self):
    self.widget.resize(100, 100)
    self.assertEqual(self.widget.getSize(), (100, 100))
#测试
if __name__ == "__main__":
  #构造测试集
  suite = unittest.TestSuite()
  suite.addTest(WidgetTestCase("testSize"))
  suite.addTest(WidgetTestCase("testResize"))
  #执行测试
  runner = unittest.TextTestRunner()
  runner.run(suite)

使用如下命令执行该单元测试:

$python text_runner.py

Python单元测试实例详解

默认情况下,TextTestRunner将结果输出到sys.stdout/sys.stderr上,但是如果在创建TextTestRunner类实例时将一个文件对象传递给了构造函数,则输出结果将被重定向到该文件中。

五、大道至简main()

PyUnit模块中定义了一个名为main的全局方法,使用它可以很方便地将一个单元测试模块变成可以直接运行的测试脚本,main()方法使用TestLoader类来搜索所有包含在该模块中的测试方法,并自动执行它们。如果Python程序员能够按照约定(以test开头)来命令所有的测试方法,那么只需要在测试模块的最后加入如下几行代码即可:

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

下面是利用main()方法来进行测试的完整例子:

#main_runner.py
from widget import Widget
import unittest
#执行测试的类
class WidgetTestCase(unittest.TestCase):
  def setUp(self):
    self.widget = Widget()
  def tearDown(self):
    self.widget.dispose()
    self.widget = None
  def testSize(self):
    self.assertEqual(self.widget.getSize(), (40, 40))
  def testResize(self):
    self.widget.resize(100, 100)
    self.assertEqual(self.widget.getSize(), (100, 100))
#测试
if __name__ == "__main__":
  unittest.main()

使用如下命令执行上面的单元测试:

$python main_runner.py

如上这样将执行WidgetTestCase中的所有测试方法,但是如果只想执行testSize()方法,则可以如下这般:

$python main_runner.py WidgetTestCase.testSize

如果在单元测试脚本中定义了TestSuite,还可以指定要运行的测试集,使用-h参数可以查看运行该脚本所有可能用到的参数:

$python main_runner.py -h

Python单元测试实例详解

需要注意的是:PyUnit的TestCase中如果有多个test_xxx,则默认按照xxx的字母顺序执行测试用例函数,如果test_xxx之间有依赖关系的话就会出错,解决方法有二:1、解耦;2、编写xxx函数时人为地按字母顺序。

当然,如果你安装了Python 2.7.2及以上版本,你还可以利用discover函数来自动发现并执行测试用例:

$python2.7 -m unittest discover

希望本文所述对大家Python程序设计有所帮助。

Python 相关文章推荐
简单说明Python中的装饰器的用法
Apr 24 Python
python数据清洗系列之字符串处理详解
Feb 12 Python
python: 自动安装缺失库文件的方法
Oct 22 Python
Python人脸识别第三方库face_recognition接口说明文档
May 03 Python
python绘制评估优化算法性能的测试函数
Jun 25 Python
详解Python用三种方式统计词频的方法
Jul 29 Python
利用Python复制文件的9种方法总结
Sep 02 Python
python实现的登录与提交表单数据功能示例
Sep 25 Python
python模块hashlib(加密服务)知识点讲解
Nov 25 Python
python实现飞船大战
Apr 24 Python
Python函数的迭代器与生成器的示例代码
Jun 18 Python
Django 用户认证Auth组件的使用
Nov 30 Python
python 请求服务器的实现代码(http请求和https请求)
May 25 #Python
django将图片上传数据库后在前端显式的方法
May 25 #Python
python3.6.3+opencv3.3.0实现动态人脸捕获
May 25 #Python
Django1.9 加载通过ImageField上传的图片方法
May 25 #Python
python matplotlib 在指定的两个点之间连线方法
May 25 #Python
基于python OpenCV实现动态人脸检测
May 25 #Python
使用matplotlib画散点图的方法
May 25 #Python
You might like
php5数字型字符串加解密代码
2008/04/24 PHP
PHP发明人谈MVC和网站设计架构 貌似他不支持php用mvc
2011/06/04 PHP
Drupal7连接多个数据库及常见问题解决
2014/03/02 PHP
php生成gif动画的方法
2015/11/05 PHP
Javascript 个人笔记(没有整理,很乱)
2007/07/07 Javascript
ImageFlow可鼠标控制图片滚动
2008/01/30 Javascript
jQuery动态加载css文件实现方法
2016/06/15 Javascript
详细解读Jquery各Ajax函数($.get(),$.post(),$.ajax(),$.getJSON())
2016/08/15 Javascript
对js eval()函数的一些见解
2016/08/15 Javascript
详解Node.js:events事件模块
2016/11/24 Javascript
xmlplus组件设计系列之选项卡(Tabbar)(5)
2017/05/03 Javascript
Jquery把获取到的input值转换成json
2017/05/15 jQuery
JavaScript对JSON数据进行排序和搜索
2017/07/24 Javascript
seajs中模块依赖的加载处理实例分析
2017/10/10 Javascript
webpack-dev-server自动更新页面方法
2018/02/22 Javascript
swiper Scrollbar滚动条组件详解
2019/09/08 Javascript
JavaScript鼠标拖拽事件详解
2020/04/03 Javascript
Jquery cookie插件实现原理代码解析
2020/08/04 jQuery
Vue 同步异步存值取值实现案例
2020/08/05 Javascript
Python文件及目录操作实例详解
2015/06/04 Python
python中 logging的使用详解
2017/10/25 Python
python编程使用selenium模拟登陆淘宝实例代码
2018/01/25 Python
Python使用Selenium模块实现模拟浏览器抓取淘宝商品美食信息功能示例
2018/07/18 Python
python+mysql实现教务管理系统
2019/02/20 Python
Python爬取知乎图片代码实现解析
2019/09/17 Python
基于Python+Appium实现京东双十一自动领金币功能
2019/10/31 Python
详解opencv中画圆circle函数和椭圆ellipse函数
2019/12/27 Python
工商管理毕业生推荐信
2013/12/24 职场文书
生产部厂长职位说明书
2014/03/03 职场文书
家教广告词
2014/03/19 职场文书
竞争上岗演讲稿范文
2014/05/12 职场文书
关于学习的决心书
2015/02/05 职场文书
试用期自我评价怎么写
2015/03/10 职场文书
年终工作总结范文
2019/06/20 职场文书
再次探讨go实现无限 buffer 的 channel方法
2021/06/13 Golang
Python使用DFA算法过滤内容敏感词
2022/04/22 Python