返回介绍

建议73:理解单元测试概念

发布于 2024-01-30 22:19:09 字数 4091 浏览 0 评论 0 收藏 0

单元测试用来验证程序单元的正确性,一般由开发人员完成,是测试过程的第一个环节,以确保所写的代码符合软件需求和遵循开发目标。好的单元测试可以带来以下好处:

减少了潜在bug,提高了代码的质量。经过了严格的单元测试,意味着代码中潜在的bug数量大大减少,同时单元测试能够使得你的思考方式不同于编码,因此能够很快发现不合理的设计或者逻辑,以及算法方面的漏洞或者故障,从而为编写高质量代码提供基本保障。

大大缩减软件修复的成本。我们知道在软件开发生命周期越早阶段发现问题或缺陷,其修复的代价越小,因此在单元测试阶段发现问题后修复代价要远远小于在集成测试或者系统测试阶段的代价。

为集成测试提供基本保障。单元测试可以大大减少程序中各个部件的不可靠性,通过先测试程序部件再测试部件组装,使集成测试变得更加简单,测试人员因此可以将更多的精力放在用户场景上。

纵然单元测试有各种好处,事实却往往是“理想很丰满,现实很骨感”。实际应用中,单元测试的实践并不理想,原因是多方面的:一则管理层重视不够,根本没有把单元测试提升到和系统集成测试同样的高度;二则是迫于项目期限的压力,开发人员往往没有更多的时间来写单元测试的用例和代码;三则开发人员本身存有趋利避害的侥幸心理,他们更关注于可以工作的代码,一旦编码完成,便迫切地希望能够进行集成工作,因为这样进度看起来更快,同时寄希望于集成测试去发现程序中潜在的问题。那么,到底应该怎么样进行有效的单元测试呢?有效的单元测试应该从以下几个方面考虑:

1)测试先行,遵循单元测试步骤。测试不应该是编码结束后再来考虑的事情,实际上从项目需求阶段就应该开始考虑。编写单元测试应该尽量安排在项目的早期,并且测试代码应该先于被测试的代码,这样更有利于明确需求。典型的单元测试的步骤如下:

创建测试计划(Test Plan)。

编写测试用例,准备测试数据。

编写测试脚本。

编写被测代码,在代码完成之后执行测试脚本。

修正代码缺陷,重新测试直到代码可接受为止。

2)遵循单元测试基本原则。常见的原则如下:

一致性。意味着1000次执行和一次执行的结果应该是一样的,因此,类似于currenttime=time.localtime(),产生这种不确定执行结果的语句应该尽量避免。

原子性。意味着单元测试的执行结果返回只有两种,True或者False,不存在部分成功、部分失败的例子。如果发生这样的情况,往往是测试设计得不够合理。

单一职责。测试应该基于情景(scenario)和行为,而不是方法。如果一个方法对应着多种行为,应该有多个测试用例;而一个行为即使对应多个方法也只能有一个测试用例。例如下边的代码。

testMethod():
  assertTrue(behaviour1)
  assertTrue(behaviour2)

应该改为:

testMethodCheckBehaviour1
():
  assertTrue(behaviour1)
testMethodCheckBehaviour2
():
  assertTrue(behaviour2)

隔离性。不能依赖于具体的环境设置,如数据库的访问、环境变量的设置、系统的时间等;也不能依赖于其他的测试用例以及测试执行的顺序,并且无条件逻辑依赖。单元测试所有的输入应该是确定的,方法的行为和结果应是可以预测的。因此要避免以下的测试例子:

testMehodBeforeOrAfter():
  if before:
      assertTrue(behaviour1)
  elif after:
      assertTrue(behaviour2)
  else:
      assertTrue(behaviour3)

修改为:

testMethodBefore():
  before = True
  assertTrue(behaviour1)
testMethodAfter():
  after= True
  assertTrue(behaviour2)
testMethodNow():
  after= False
  before = False
  assertTrue(behaviour3)

3)使用单元测试框架。Python测试也曾经历过“蛮荒时代”,那个时候测试完全是个人化的行为,没有统一的框架标准,每个用Python构建的项目在编写和运行测试方面都采用自己的习惯做法。这种做法不仅效率低下,而且不利于项目管理。幸好后来Python社区出现了一些测试套件,提供约定和通用标准,后面逐渐演变为流行的测试框架。在单元测试方面常见的测试框架有PyUnit等,它是Kent Beck和Erich Gamma所设计的JUnit的Python版本,在Python 2.1之前需要单独安装,Python 2.1之后它成为一个标准库,名为unittest。它支持单元测试自动化,可以共享地进行测试环境的设置和清理,支持测试用例的聚集以及独立的测试报告框架。我们以unittest来看看如何借助单元测试框架更好地进行单元测试。unittest相关的概念主要有以下4个:

测试固件(test fixtures)。测试相关的准备工作和清理工作,基于类TestCase创建测试固件的时候通常需要重新实现setUp()和tearDown()方法。当定义了这些方法的时候,测试运行器会在运行测试之前和之后分别调用这两个方法。

测试用例(test case)。最小的测试单元,通常基于TestCase构建。

测试用例集(test suite)。测试用例的集合,使用TestSuite类来实现,除了可以包含TestCase外,也可以包含其他TestSuite。

测试运行器(test runner)。控制和驱动整个单元测试过程,一般使用TestRunner类作为测试用例的基本执行环境,常用的运行器为TextTestRunner,它是TestRunner的子类,以文字方式运行测试并报告结果。

来看一个简单实例。假设要测试下述类:

class MyCal(object):
     def add(self,a,b):
         return a+b
     def sub(self,a,b):
         return a-b

首先编写测试用例,并在setUp()方法中完成初始化工作,在tearDown()方法中完成资源释放相关的工作。我们采用动态方法编写测试类,多个测试方法可以集成在一个类中,这些方法按习惯通常以test开头。具体如下:

class MyCalTest(unittest.TestCase):
  def setUp(self):
    print "running set up"
    self.mycal = mycal.MyCal()
  def tearDown(self):
    print "running teardown"
    self.mycal = None
  def testAdd(self):
    self.assertEqual(self.mycal.add(-1,7), 6)
  def testSub(self):
    self.assertEqual(self.mycal.sub(10,2), 8)

在创建了一些TestCase子类的实例作为测试用例之后,下一步要做的工作就是用TestSuit类来组织它们。TestSuite类可以看成是TestCase类的一个容器,用来对多个测试用例进行组织,这样多个测试用例可以自动在一次测试中全部完成。

  suite = unittest.TestSuite()
  suite.addTest(MyCalTest("testAdd"))
  suite.addTest(MyCalTest("testSub"))

在编写完测试用例及组织好测试用例之后,现在可以执行测试了。

  runner = unittest.TextTestRunner()
  runner.run(suite)

运行命令python-m unittest-v MyCalTest,得到测试结果的输出如下:

testAdd (MyCalTest.MyCalTest) ... running set up
running teardown
ok
testSub (MyCalTest.MyCalTest) ... running set up
running teardown
ok
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK

当然实际执行测试过程和应用测试框架比上面的例子要复杂得多。读者可以在Python文档中查看更多关于unittest使用的详细信息,并在实际工作中实践。

最后需要强调的是,单元测试绝不是浪费时间的无用功,它是高质量代码的保障之一,在软件开发的环节中值得投入精力和时间把好这一关。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文