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模块学习 datetime介绍
Aug 27 Python
python制作一个桌面便签软件
Aug 09 Python
python模块之paramiko实例代码
Jan 31 Python
Python模拟随机游走图形效果示例
Feb 06 Python
Python装饰器用法实例分析
Jan 14 Python
用python 实现在不确定行数情况下多行输入方法
Jan 28 Python
Python脚本修改阿里云的访问控制列表的方法
Mar 08 Python
python3下载抖音视频的完整代码
Jun 05 Python
Django自定义用户登录认证示例代码
Jun 30 Python
vscode配置anaconda3的方法步骤
Aug 08 Python
python+requests接口自动化框架的实现
Aug 31 Python
python 进制转换 int、bin、oct、hex的原理
Jan 13 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中使用redis
2013/11/04 PHP
解决php表单重复提交实现方法
2015/09/29 PHP
php版微信开发之接收消息,自动判断及回复相应消息的方法
2016/09/23 PHP
Extjs 几个方法的讨论
2010/01/28 Javascript
js 面向对象的技术创建高级 Web 应用程序
2010/02/25 Javascript
JavaScript高级程序设计(第3版)学习笔记7 js函数(上)
2012/10/11 Javascript
javascript之典型高阶函数应用介绍
2013/01/10 Javascript
JS实现程序暂停与继续功能代码解读
2013/10/10 Javascript
JavaScript sub方法入门实例(把字符串显示为下标)
2014/10/17 Javascript
javascript动态创建链接的方法
2015/05/13 Javascript
jquery实现两个图片渐变切换效果的方法
2015/06/25 Javascript
购物车前端开发(jQuery和bootstrap3)
2016/08/27 Javascript
Bootstrap中datetimepicker使用小结
2016/12/28 Javascript
详解如何使用webpack+es6开发angular1.x
2017/08/16 Javascript
Vue+Element UI+Lumen实现通用表格分页功能
2019/02/02 Javascript
Vue图片浏览组件v-viewer用法分析【支持旋转、缩放、翻转等操作】
2019/11/04 Javascript
[40:19]2018完美盛典CS.GO表演赛
2018/12/17 DOTA
[09:59]DOTA2-DPC中国联赛2月7日Recap集锦
2021/03/11 DOTA
Python实现3行代码解简单的一元一次方程
2014/08/18 Python
在Python中进行自动化单元测试的教程
2015/04/15 Python
Python Socket使用实例
2017/12/18 Python
Ubuntu下使用Python实现游戏制作中的切分图片功能
2018/03/30 Python
Python快速查找list中相同部分的方法
2018/06/27 Python
Python实现带参数的用户验证功能装饰器示例
2018/12/14 Python
Python 普通最小二乘法(OLS)进行多项式拟合的方法
2018/12/29 Python
Python模块相关知识点小结
2020/03/09 Python
解决pyinstaller打包运行程序时出现缺少plotly库问题
2020/06/02 Python
Pandas把dataframe或series转换成list的方法
2020/06/14 Python
Python sublime安装及配置过程详解
2020/06/29 Python
canvas画图被放大且模糊的解决方法
2020/08/11 HTML / CSS
英国受欢迎的运动鞋和街头服装商店:Footasylum
2018/06/12 全球购物
欧洲、亚洲、非洲和拉丁美洲的度假套餐:Great Value Vacations
2019/03/30 全球购物
喜之郎果冻广告词
2014/03/20 职场文书
反洗钱宣传活动总结
2014/08/26 职场文书
船舶调度指挥系统——助力智慧海事
2022/02/18 无线电
python实现对doc、txt、xls等文档的读写操作
2022/04/02 Python