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实现的简单RPG游戏流程实例
Jun 28 Python
Python入门之三角函数sin()函数实例详解
Nov 08 Python
python pandas.DataFrame选取、修改数据最好用.loc,.iloc,.ix实现
Jun 11 Python
python实现自动登录后台管理系统
Oct 18 Python
python如何查看微信消息撤回
Nov 27 Python
对python 多个分隔符split 的实例详解
Dec 20 Python
eclipse创建python项目步骤详解
May 10 Python
Numpy 中的矩阵求逆实例
Aug 26 Python
解决TensorFlow程序无限制占用GPU的方法
Jun 30 Python
Python Opencv轮廓常用操作代码实例解析
Sep 01 Python
python实现ping命令小程序
Dec 28 Python
Python中的turtle画箭头,矩形,五角星
Mar 16 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
如何利用php array_multisort函数 对数据库结果进行复杂排序
2013/06/08 PHP
解析PHP中empty is_null和isset的测试
2013/06/29 PHP
smarty内置函数capture用法分析
2015/01/22 PHP
php数组指针函数功能及用法示例
2020/02/11 PHP
JavaScript之引用类型介绍
2012/08/10 Javascript
jquery插件lazyload.js延迟加载图片的使用方法
2014/02/19 Javascript
jquery删除数据记录时的弹出提示效果
2014/05/06 Javascript
Javascript基础教程之关键字和保留字汇总
2015/01/18 Javascript
Jquery中offset()和position()的区别分析
2015/02/05 Javascript
JavaScript获取当前运行脚本文件所在目录的方法
2016/02/03 Javascript
前端 Vue.js 和 MVVM 详细介绍
2016/12/29 Javascript
vue2.0实现分页组件的实例代码
2017/06/22 Javascript
javascript 中select框触发事件过程的分析
2017/08/01 Javascript
Three.js基础学习之场景对象
2017/09/27 Javascript
vue + vuex todolist的实现示例代码
2018/03/09 Javascript
vue组件数据传递、父子组件数据获取,slot,router路由功能示例
2019/03/19 Javascript
前端性能优化建议
2020/09/17 Javascript
python处理圆角图片、圆形图片的例子
2014/04/25 Python
Python中使用MELIAE分析程序内存占用实例
2015/02/18 Python
一波神奇的Python语句、函数与方法的使用技巧总结
2015/12/08 Python
利用Python生成文件md5校验值函数的方法
2017/01/10 Python
Python numpy 点数组去重的实例
2018/04/18 Python
Python 数值区间处理_对interval 库的快速入门详解
2018/11/16 Python
Python的垃圾回收机制详解
2019/08/28 Python
css3打造一款漂亮的卡哇伊按钮
2013/03/20 HTML / CSS
美国地毯购买网站:Rugs USA
2019/02/23 全球购物
应届生煤化工求职信
2013/10/21 职场文书
经理秘书岗位职责
2013/11/14 职场文书
小小商店教学反思
2014/04/27 职场文书
消防安全承诺书
2014/05/22 职场文书
2015少先队大队辅导员工作总结
2015/07/24 职场文书
详解Html5项目适配系统深色模式方案总结
2021/04/14 HTML / CSS
python使用pymysql模块操作MySQL
2021/06/16 Python
MySQL系列之七 MySQL存储引擎
2021/07/02 MySQL
Python 处理表格进行成绩排序的操作代码
2021/07/26 Python
详解Nginx的超时keeplive_timeout配置步骤
2022/05/25 Servers