Python单元测试框架

发表于:2015-1-04 10:23

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:Steve Purcell    来源:51Testing软件测试网采编

  幸运的是,我们可以将这些设置代码提取出来并放置在一个叫做setUp的 钩子方法(hook method)中。测试框架会在运行测试时自动调用此方法:
  import unittest
  class SimpleWidgetTestCase(unittest.TestCase):
  def setUp(self):
  self.widget = Widget("The widget")
  class DefaultWidgetSizeTestCase(SimpleWidgetTestCase):
  def runTest(self):
  assert self.widget.size() == (50,50), 'incorrect default size'
  class WidgetResizeTestCase(SimpleWidgetTestCase):
  def runTest(self):
  self.widget.resize(100,150)
  assert self.widget.size() == (100,150), \
  'wrong size after resize'
  如果setUp方法在测试运行时抛出异常,框架会认为测试遇到了错误并且 runTest不会被执行。
  类似的,我们也可以提供一个tearDown方法来完成在runTest运行之后的清理工作:
  import unittest
  class SimpleWidgetTestCase(unittest.TestCase):
  def setUp(self):
  self.widget = Widget("The widget")
  def tearDown(self):
  self.widget.dispose()
  self.widget = None
  如果setUp执行成功, 那么无论runTest是否成功,tearDown方法都将被执行。
  Such a working environment for the testing code is termed a fixture. 这个测试代码的运行环境被称为固件(fixture,译者注:此为暂定译法,意为固定的构件或方法)。
  包含多个测试方法的测试用例类
  很多小型测试用例经常会使用相同的固件。在这个用例中,我们最终从SimpleWidgetTestCase继承产生很多仅包含一个方法的类,如 DefaultWidgetSizeTestCase。这是很耗时且不被鼓励的,因此,沿用JUnit的风格,PyUnit提供了一个更简便的方法:
  import unittest
  class WidgetTestCase(unittest.TestCase):
  def setUp(self):
  self.widget = Widget("The widget")
  def tearDown(self):
  self.widget.dispose()
  self.widget = None
  def testDefaultSize(self):
  assert self.widget.size() == (50,50), 'incorrect default size'
  def testResize(self):
  self.widget.resize(100,150)
  assert self.widget.size() == (100,150), \
  'wrong size after resize'
  在这个用例中,我们没有提供runTest方法,而是两个不同的测试方法。类实例将创建和销毁各自的self.widget并运行某一个test方法。 当创建类实例时,我们必须通过向构造器传递方法的名称来指明哪个测试方法将被运行:
  defaultSizeTestCase = WidgetTestCase("testDefaultSize")
  resizeTestCase = WidgetTestCase("testResize")
  将测试用例聚合成测试套件
  测试用例实例可以根据它们所测试的特性组合到一起。PyUnit为此提供了一个机制叫做”测试套件“(test suite)。它由unittest模块中的TestSuite类表示:
  widgetTestSuite = unittest.TestSuite()
  widgetTestSuite.addTest(WidgetTestCase("testDefaultSize"))
  widgetTestSuite.addTest(WidgetTestCase("testResize"))
  我们稍后会看到,在每个测试模块中提供一个返回已创建测试套件的可调用对象,会是一个使测试更加便捷的好方法:
  def suite():
  suite = unittest.TestSuite()
  suite.addTest(WidgetTestCase("testDefaultSize"))
  suite.addTest(WidgetTestCase("testResize"))
  return suite
  甚至可写成:
  class WidgetTestSuite(unittest.TestSuite):
  def __init__(self):
  unittest.TestSuite.__init__(self,map(WidgetTestCase,
  ("testDefaultSize",
  "testResize")))
  (诚然,第二种方法不是为胆小者准备的)
  因为创建一个包含很多相似名称的测试方法的TestCase子类是一种很常见的模式,所以unittest模块提供一个便捷方法,makeSuite,来 创建一个由测试用例类内所有测试用例组成的测试套件:
  suite = unittest.makeSuite(WidgetTestCase,'test')
  需要注意的是,当使用makeSuite方法时,测试套件运行每个测试用例的顺序是由测试方法名根据Python内建函数cmp所排序的顺序而决定的。
  嵌套测试套件
  我们经常希望将一些测试套件组合在一起来一次性的测试整个系统。这很简单,因为多个TestSuite可以被加入进另一个TestSuite,就如同 多个TestCase被加进一个TestSuite中一样:
  suite1 = module1.TheTestSuite()
  suite2 = module2.TheTestSuite()
  alltests = unittest.TestSuite((suite1, suite2))
  在发布的软件包中的“examples”目录中,"alltests.py”提供了使用嵌套测试套件的例子
  测试代码放置位置
  你可以将测试用例定义与被测试代码置于同一个模块中(例如“widget.py”),但是将测试代码放置在单独的模块中(如“widgettests.py”)会有一些优势:
  测试模块可以从命令行单独执行
  测试代码可以方便地从发布代码中分离
  少了在缺乏充足理由的情况下为适应被测试代码而更改测试代码的诱惑
  相对于被测试代码,测试代码不应该被频繁的修改
  被测试代码可以更方法的进行重构
  既然C语言代码的测试应该置于单独的模块,那何不保持这个一致性呢?
  如果测试策略改变,也无需修改被测试源代码
  交互式运行测试
  我们编写测试的主要目的是运行它们并检查我们的软件是否工作正常。测试框架使用“TestRunner”类来为运行测试提供环境。最常用的TestRunner是TextTestRunner, 它可以以文字方式运行测试并报告结果:
  runner = unittest.TextTestRunner()
  runner.run(widgetTestSuite)
  TextTestRunner默认将输出发送到sys.stderr,但是你可以通过向它的构造器传递一个不同的类似文件(file-object)对象来改变默认方式。
  如需在Python解释器会话中运行测试,这样使用TextTestRunner是一个理想的方法。
  从命令行运行测试
  unittest模块包含一个main方法,可以方便地将测试模块转变为可以运行测试的脚本。main 使用unittest.TestLoader类来自动查找和加载模块内测试用例。
  因此,如果你之前已经使用test*惯例对测试方法进行命名,那么你就可以将以下代码插入测试模块的结尾:
  if __name__ == "__main__":
  unittest.main()
  这样,当你从命令行执行你的测试模块时,其所包含的所有测试都将被运行。使用“-h”选项运行模块可以查看所有可用的选项。
  如需从命令行运行任意测试,你可以将unittest模块作为脚本运行,并将所需执行的测试套件中的测试用例名称作为参数传递给此脚本:
  % python unittest.py widgettests.WidgetTestSuite
  or
  % python unittest.py widgettests.makeWidgetTestSuite
  你还可以在命令行指明特定的测试(方法)来执行。如要运行“listtests”模块中的TestCase类的子类 'ListTestCase'(参见发布软件包中的“examples”子目录), 你可以执行以下命令:
  % python unittest.py listtests.ListTestCase.testAppend
  “testAppend”是测试用例实例将要执行的测试方法的名称。你可以执行以下代码来创建ListTestCase类实例并执行其所包含的所有“test*”测试方法:
  % python unittest.py listtests.ListTestCase
  在用户界面窗口运行测试
  你还可以使用图形化窗口运行你的测试。它是用Tkinter编写的。在多数平台上,这个窗口工具与Python是捆绑在一起发布的。它看上去和JUnit窗口很相似。
  你只需运行以下命令来使用测试运行窗口:
  % python unittestgui.py
  or
  % python unittestgui.py widgettests.WidgetTestSuite
  这里需要注意的是,所输入的测试名称必须是一个可以返回TestCase或TestSuite类实例的对象名称,不可以是事先创建好的测试名称, 因为每个测试必须在每次运行是重新创建。
  使用窗口测试会因为更新那些窗口而带来额外的时间开销。在我系统上,每一千个测试,它会多花七秒钟。你的消耗可能会不同。
  为测试编写文档
  通常当测试运行时,TestRunner将显示其名称。这个名称是由测试用例类名和所运行的测试方法名组成的。
  但是如果你为测试方法提供了doc-string,则当测试运行时,doc-string的第一行将被显示出来。这为编写测试文档提供了一个很便捷的机制:
  class WidgetTestCase(unittest.TestCase):
  def testDefaultSize(self):
  """Check that widgets are created with correct default size"""
  assert self.widget.size() == (50,50), 'incorrect default size'
32/3<123>
《2023软件测试行业现状调查报告》独家发布~

精彩评论

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号