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自动生成文章
Dec 25 Python
python高手之路python处理excel文件(方法汇总)
Jan 07 Python
python PIL模块与随机生成中文验证码
Feb 27 Python
Python连接phoenix的方法示例
Sep 29 Python
浅析Git版本控制器使用
Dec 10 Python
python入门前的第一课 python怎样入门
Mar 06 Python
在django中,关于session的通用设置方法
Aug 06 Python
使用python matplotlib 画图导入到word中如何保证分辨率
Apr 16 Python
python开发前景如何
Jun 11 Python
Python打印不合法的文件名
Jul 31 Python
Python Matplotlib绘图基础知识代码解析
Aug 31 Python
python3 字符串str和bytes相互转换
Mar 23 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函数
2006/12/06 PHP
收藏的一个php小偷的核心程序
2007/04/09 PHP
php 用checkbox一次性删除多条记录的方法
2010/02/23 PHP
PHP获取搜索引擎关键字来源的函数(支持百度和谷歌等搜索引擎)
2012/10/03 PHP
Thinkphp中Create方法深入探究
2014/06/16 PHP
合并ThinkPHP配置文件以消除代码冗余的实现方法
2014/07/22 PHP
Firefox下无法正常显示年份的解决方法
2014/09/04 Javascript
jQuery操作JSON的CRUD用法实例
2015/02/25 Javascript
解决WordPress使用CDN后博文无法评论的错误
2015/12/15 Javascript
ES6的新特性概览
2016/03/10 Javascript
使用jQuery实现Web页面换肤功能的要点解析
2016/05/12 Javascript
JavaScript sort数组排序方法和自我实现排序方法小结
2016/06/06 Javascript
AngularJS基础 ng-include 指令示例讲解
2016/08/01 Javascript
nodejs socket实现的服务端和客户端功能示例
2017/06/02 NodeJs
Mac 安装 nodejs方法(图文详细步骤)
2017/10/30 NodeJs
vue axios请求拦截实例代码
2018/03/29 Javascript
在 Vue.js中优雅地使用全局事件的方法
2019/02/01 Javascript
Javascript三种字符串连接方式及性能比较
2019/05/28 Javascript
vue项目中自定义video视频控制条的实现代码
2020/04/26 Javascript
JavaScript 实现轮播图特效的示例
2020/11/05 Javascript
[00:12]2018DOTA2亚洲邀请赛 sylar表现SOLO技艺
2018/04/06 DOTA
Python中使用hashlib模块处理算法的教程
2015/04/28 Python
python paramiko模块学习分享
2017/08/23 Python
python实现彩票系统
2020/06/28 Python
使用django实现一个代码发布系统
2019/07/18 Python
python按修改时间顺序排列文件的实例代码
2019/07/25 Python
Atom Python 配置Python3 解释器的方法
2019/08/28 Python
django model的update时auto_now不被更新的原因及解决方式
2020/04/01 Python
Python同时迭代多个序列的方法
2020/07/28 Python
努比亚手机官网:nubia
2016/10/06 全球购物
VC++笔试题
2014/10/13 面试题
运动会广播稿30字
2014/01/21 职场文书
2014年教师业务学习材料
2014/05/12 职场文书
专科生就业求职信
2014/06/22 职场文书
交通事故协议书范本
2016/03/19 职场文书
mysql sql常用语句大全
2022/06/21 MySQL