




已阅读5页,还剩7页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
一、软件测试 大型软件系统的开发是一个很复杂的过程, 其中因为人的因素而所产生的错误非 常多, 因此软件在开发过程必须要有相应的质量保证活动,而软件测试则是保证 质量的关键措施。正像软件熵(software entropy)所描述的那样:一个程序从 设计很好的状态开始, 随着新的功能不断地加入, 程序逐渐地失去了原有的结构, 最终变成了一团乱麻(其实最初的“很好的状态“得加个问号)。测试的目的说起 来其实很简单也极具吸引力,那就是写出高质量的软件并解决软件熵这一问题。 可惜的是,软件开发人员很少能在编码的过程中就进行软件测试,大部分软件项 目都只在最终验收时才进行测试,有些项目甚至根本没有测试计划!随着软件质 量意识的增强, 许多软件开发组织开始转向 UML、 CMM、 RUP、 XP 等软件工程方法, 以期提高软件质量,并使软件开发过程更加可控,好在这些方法对测试都提出了 很严格的要求,从而使得测试在软件开发过程的作用开始真正体现出来。 软件测试作为一种系统工程,涉及到整个软件开发过程的各个方面,需要管理人 员、设计人员、开发人员和测试人员的共同努力。作为软件开发过程中的主要力 量,现今的程序员除了要编写实现代码外,还承担着单元测试这一艰巨任务,因 此必须采用新的工作模式: 编写和维护一套详尽的单元测试用例; 先构造单元测试和验收测试用例,然后再编写代码; 根据构造的测试用例来编写代码。 单元测试负责对最小的软件设计单元(模块)进行验证,它使用软件设计文档中 对模块的描述作为指南,对重要的程序分支进行测试以发现模块中的错误。由于 软件模块并不是一个单独的程序, 为了进行单元测试还必须编写大量额外的代码, 从而无形中增加了开发人员的工作量, 目前解决这一问题比较好的方法是使用测 试框架。测试框架是在用 XP 方法进行单元测试时的关键,尤其是在需要构造大 量测试用例时更是如此,因为如果完全依靠手工的方式来构造和执行这些测试, 肯定会变成一个花费大量时间并且单调无味的工作, 而测试框架则可以很好地解 决这些问题。 使用 Python 语言的开发人员可以使用 Steve Purcell 编写的 PyUnit 作为单元测 试框架,通过将单元测试融合到 PyUnit 这一测试框架里,Python 程序员可以更 容易地增加、管理、执行测试用例,并对测试结果进行分析。此外,使用 PyUnit 还可以实现自动单元测试(回归测试)。 二、规范 Python 单元测试 测试是一个贯穿于整个开发过程的连续过程,从某个意义上说,软件开发的过程 实际上就是测试过程。正如 Martin Fowler 所说的“在你不知道如何测试代码之 前,就不该编写程序。而一旦你完成了程序,测试代码也应该完成。除非测试成 功,你不能认为你编写出了可以工作的程序。“ 测试最基本的原理就是比较预期结果是否与实际执行结果相同, 如果相同则测试 成功,否则测试失败。为了更好地理解 PyUnit 这一自动测试框架的作用,先来 看一个简单的例子,假设我们要对例 1 中的 Widget 类进行测试: 例 1. widget.py # 将要被测试的类 class Widget: def _init_(self, size = (40, 40): self._size = size def getSize(self): return self._size def resize(self, width, height): if width0or height 0: raise ValueError, “illegal size“ self._size = (width, height) def dispose(self): pass 采用手工方式进行单元测试的 Python 程序员很可能会写出类似例 2 的测试代码 来, 例 2. manual.py from widget import Widget # 执行测试的类 class TestWidget: def testSize(self): expectedSize = (40, 40); widget = Widget() if widget.getSize() = expectedSize: print “test Widget: getSize works perfected!“ else: print “test Widget: getSize doesnt work!“ # 测试 if _name_ = _main_: myTest = TestWidget() myTest.testSize() 稍一留心你不难发现这种手工测试方法存在许多问题。首先,测试程序的写法没 有一定的规范可以遵循,十个程序员完全可能写出十种不同的测试程序来,如果 每个 Python 程序员都有自己不同的设计测试类的方法,光维护被测试的类就够 麻烦了,谁还顾得上维护测试类。其次,需要编写大量的辅助代码才能进行单元 测试, 例 1 中用于测试的代码甚至比被测试的代码还要多,而这毫无疑问将增大 Python 程序员的工作量。 为了让单元测试代码能够被测试和维护人员更容易地理解, 最好的解决办法是让 开发人员遵循一定的规范来编写用于测试的代码,具体到 Python 程序员来讲, 则是要采用 PyUnit 这一自动测试框架来构造单元测试用例。 目前 PyUnit 已经得 到了大多数 Python 开发人员的认可,成了事实上的单元测试标准。如果采用 PyUnit 来进行同样的测试,则测试代码将如例 3 所示: 例 3. auto.py from widget import Widget import unittest # 执行测试的类 class WidgetTestCase(unittest.TestCase): def setUp(self): self.widget = Widget() def tearDown(self): self.widget = None def testSize(self): self.assertEqual(self.widget.getSize(), (40, 40) # 构造测试集 def suite(): suite = unittest.TestSuite() suite.addTest(WidgetTestCase(“testSize“) return suite # 测试 if _name_ = “_main_“: unittest.main(defaultTest = suite) 在采用 PyUnit 这一单元测试框架后,用于测试的代码做了相应的改动: 用 import 语句引入 unittest 模块。 让所有执行测试的类都继承于 TestCase 类, 可以将 TestCase 看成是对特 定类进行测试的方法的集合。 在 setUp()方法中进行测试前的初始化工作, 并在 tearDown()方法中执行 测试后的清除工作,setUp()和 tearDown()都是 TestCase 类中定义的方 法。 在 testSize()中调用 assertEqual()方法,对 Widget 类中 getSize()方 法的返回值和预期值进行比较,确保两者是相等的,assertEqual()也是 TestCase 类中定义的方法。 提供名为 suite()的全局方法,PyUnit 在执行测试的过程调用 suit()方 法来确定有多少个测试用例需要被执行, 可以将 TestSuite 看成是包含所 有测试用例的一个容器。 虽然看起来有点复杂, 但 PyUnit 使得所有的 Python 程序员都可以使用同样的单 元测试方法,测试过程不再是杂乱无章的了,而是在同一规范指导下进行的有序 行为,这就是使用 PyUnit 这一自动单元测试框架所带来的最大好处。 三、自动测试框架 PyUnit 在对软件测试理论和 PyUnit 有了一个大致了解之后,下面辅以具体的实例介绍 Python 程序员如何借助 PyUnit 来进行单元测试。所有的代码均在 Python 2.2.2 下调试通过,操作系统使用的是 Red Hat Linux 9。 3.1 安装 在 Python 中进行单元测试时需要用到 PyUnit 模块,Python 2.1 及其以后的版 本都将 PyUnit 作为一个标准模块,但如果你使用的是较老版本的 Python,那就 要自已动手安装了。在 PyUnit 的网站 ( PyUnit 最新的源 码包,此处使用的是 pyunit-1.4.1.tar.gz。 在下载好 PyUnit 软件包后,执行下面的命令对其进行解压缩: rootgary source# tar xzvf pyunit-1.4.1.tar.gz 要在 Python 程序中使用 PyUnit 模块, 最简单的办法是确保 PyUni 软件包中的文 件 unittest.py 和 unittestgui.py 都包含在 Python 的搜索路径中, 这既可以通 过直接设置 PYTHONPATH 环境变量来实现,也可以执行以下的命令来将它们复制 到 Python 的当前搜索路径中: rootgary source# cd pyunit-1.4.1 rootgary pyunit-1.4.1# python setup.py install 3.2 测试用例 TestCase 软件测试中最基本的组成单元是测试用例(test case),PyUnit 使用 TestCase 类来表示测试用例,并要求所有用于执行测试的类都必须从该类继承。TestCase 子类实现的测试代码应该是自包含(self contained)的,也就是说测试用例既 可以单独运行,也可以和其它测试用例构成集合共同运行。 TestCase 在 PyUnit 测试框架中被视为测试单元的运行实体, Python 程序员可以 通过它派生自定义的测试过程与方法(测试单元),利用 Command 和 Composite 设计模式,多个 TestCase 还可以组合成测试用例集合。PyUnit 测试框架在运行 一个测试用例时,TestCase 子类定义的 setUp()、runTest()和 tearDown()方法 被依次执行,最简单的测试用例只需覆盖 runTest()方法来执行特定的测试代码 就可以了,如例 4 所示: 例 4. static_single.py import unittest # 执行测试的类 class WidgetTestCase(unittest.TestCase): def runTest(self): widget = Widget() self.assertEqual(widget.getSize(), (40, 40) 而要在 PyUnit 测试框架中构造上述 WidgetTestCase 类的一个实例, 应该不带任 何参数调用其构造函数: testCase = WidgetTestCase() 一个测试用例通常只对软件模块中的一个方法进行测试,采用覆盖 runTest()方 法来构造测试用例在 PyUnit 中称为静态方法,如果要对同一个软件模块中的多 个方法进行测试,通常需要构造多个执行测试的类,如例 5 所示: 例 5. static_multi.py import unittest # 测试 getSize()方法的测试用例 class WidgetSizeTestCase(unittest.TestCase): def runTest(self): widget = Widget() self.assertEqual(widget.getSize(), (40, 40) # 测试 resize()方法的测试用例 class WidgetResizeTestCase(unittest.TestCase): def runTest(self): widget = Widget() widget.resize(100, 100) self.assertEqual(widget.getSize(), (100, 100) 采用静态方法,Python 程序员不得不为每个要测试的方法编写一个测试类(该 类通过覆盖 runTest()方法来执行测试),并在每一个测试类中生成一个待测试 的对象。在为同一个软件模块编写测试用例时,很多时候待测对象有着相同的初 始状态,因此采用上述方法的 Python 程序员不得不在每个测试类中为待测对象 进行同样的初始化工作,而这往往是一项费时且枯燥的工作。 一种更好的解决办法是采用 PyUnit 提供的动态方法,只编写一个测试类来完成 对整个软件模块的测试,这样对象的初始化工作可以在 setUp()方法中完成,而 资源的释放则可以在 tearDown()方法中完成,如例 6 所示: 例 6. dynamic.py 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 测试框架指明运行该测试 用例时究竟应该调用测试类中的哪个方法: sizeTestCase = WidgetTestCase(“testSize“) resizeTestCase = WidgetTestCase(“testResize“) 3.3 测试用例集 TestSuite 完整的单元测试很少只执行一个测试用例, 开发人员通常都需要编写多个测试用 例才能对某一软件功能进行比较完整的测试, 这些相关的测试用例称为一个测试 用例集,在 PyUnit 中是用 TestSuite 类来表示的。 在创建了一些 TestCase 子类的实例作为测试用例之后,下一步要做的工作就是 用 TestSuit 类来组织它们。PyUnit 测试框架允许 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() 如果用于测试的类中所有的测试方法都以 test 开,Python 程序员甚至可以用 PyUnit 模块提供的 makeSuite()方法来构造一个 TestSuite: def suite(): return unittest.makeSuite(WidgetTestCase, “test“) 在 PyUnit 测试框架中,TestSuite 类可以看成是 TestCase 类的一个容器,用来 对多个测试用例进行组织,这样多个测试用例可以自动在一次测试中全部完成。 事实上,TestSuite 除了可以包含 TestCase 外,也可以包含 TestSuite,从而可 以构成一个更加庞大的测试用例集: suite1 = mysuite1.TheTestSuite() suite2 = mysuite2.TheTestSuite() alltests = unittest.TestSuite(suite1, suite2) 3.4 实施测试 编写测试用例(TestCase)并将它们组织成测试用例集(TestSuite)的最终目 的只有一个:实施测试并获得最终结果。PyUnit 使用 TestRunner 类作为测试用 例的基本执行环境,来驱动整个单元测试过程。Python 开发人员在进行单元测 试时一般不直接使用 TestRunner 类, 而是使用其子类 TextTestRunner 来完成测 试,并将测试结果以文本方式显示出来: runner = unittest.TextTestRunner() runner.run(suite) 使用 TestRunner 来实施测试的例子如例 7 所示, 例 7. 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) 要执行该单元测试,可以使用如下命令: xiaowpgary code$ python text_runner.py 运行结果应该如下所示,表明执行了 2 个测试用例,并且两者都通过了测试: - - Ran 2 tests in 0.000s OK 如果对数据进行修改,模拟出错的情形,将会得到如下结果: .F = FAIL: testResize (_main_.WidgetTestCase) - - Traceback (most recent call last): File “text_runner.py“, line 15, in testResize self.assertEqual(self.widget.getSize(), (200, 100) File “/usr/lib/python2.2/unittest.py“, line 286, in failUnlessEqual raise self.failureException, AssertionError: (100, 100) != (200, 100) - - Ran 2 tests in 0.001s FAILED (failures=1) 默认情况下,TextTestRunner 将结果输出到 sys.stderr 上,但如果在创建 TextTestRunner 类实例时将一个文件对象传递给了构造函数,则输出结果将被 重定向到该文件中。在 Python 的交互环境中驱动单元测试时,使用 TextTestRunner 类是一个不错的选择。 PyUnit 模块中定义了一个名为 main 的全局方法,使用它可以很方便地将一个单 元测试模块变成可以直接运行的测试脚本,main()方法使用 TestLoader 类来搜 索所有包含在该模块中的测试方法,并自动执行它们。如果 Python 程序员能够 按照约定(以 test 开头)来命名所有的测试方法,那就只需要在测试模块的最 后加入如下几行代码即可: if _name_ = “_main_“: unittest.main() 使用 main()方法来实施测试的例子如例 8 所示, 例 8. 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.w
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 建筑方案设计初步设计区别(3篇)
- 建筑发展行动方案设计思路(3篇)
- 梅林街道校园消防安全(3篇)
- 三 雷雨(节选)教学设计-2025-2026学年中职基础课-基础模块 下册-高教版(2023)-(语文)-50
- 改造建筑陕西节能方案设计(3篇)
- 八年级生物上册 6.15.1《人体内物质的运输》第3课时说课稿 (新版)苏科版
- 上海市张堰中学2025年化学高三第一学期期末学业质量监测模拟试题
- 大型商业建筑改造方案设计(3篇)
- 睡眠质量与工作绩效-洞察及研究
- 生态农业与修复结合-洞察及研究
- 2025年秋期部编版四年级上册小学语文教学计划+教学进度表
- 2025年特种设备检验检测项目合作计划书
- 安全生产投入费用明细
- 2025年司机三力考试题库及答案
- 铁路动态设计管理办法
- 奔驰GL350GL450GL550中文版说明书
- 智能微电网应用技术课件
- 泌尿外科围手术期护理
- 光谷华科附小数学试卷
- 大学班助培训
- 学校公文写作培训
评论
0/150
提交评论