揭开极端编程的神秘面纱: 测试驱动的编程

发表于:2008-2-22 15:16

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

 作者:未知    来源:网络转载

  测试驱动的编程是 XP 困扰程序员的一个方面。对于测试驱动的编程意味着什么以及如何去做,大多数人都做出了不正确的假设。这个月,XP 方面的讲师兼 Java 开发人员 Roy Miller 谈论了测试驱动的编程是什么,它为什么可以使程序员的生产力和质量发生巨大变化,以及编写测试的原理。请在与本文相随的 论坛中提出您就本文的想法,以飨笔者和其他读者。
  最近 50 年来,测试一直被视为项目结束时要做的事。当然,可以在项目进行之中结合测试,测试通常并不是在 所有编码工作结束后才开始,而是一般在稍后阶段进行测试。然而,XP 的提倡者建议完全逆转这个模型。作为一名程序员,应该在编写代码 之前编写测试,然后只编写足以让测试通过的代码即可。这样做将有助于使您的系统尽可能的简单。

先编写测试

  XP 涉及两种测试: 程序员测试和 客户测试。测试驱动的编程(也称为 测试为先编程)最常指第一种测试,至少我使用这个术语时是这样。测试驱动的编程是让 程序员测试(即单元测试 ― 重申一下,只是换用一个术语)决定您所编写的代码。这意味着您必须在编写代码之前进行测试。测试指出您 需要编写的代码,从而也 决定了您要编写的代码。您只需编写足够通过测试的代码即可 ― 不用多,也不用少。XP 规则很简单:如果不进行程序员测试,则您不知道要编写什么代码,所以您不会去编写任何代码。

如何先编写测试

  整个理论很棒,但如何 先编写测试呢?首先,我推荐您阅读 Kent Beck 撰写的 Test-Driven Development: By Example(请参阅 参考资料)一书,里面列举了一个详尽的贯穿于整本书的示例。该书不仅讲述了如何编写测试和让这些测试来驱动您的代码的原理,而且还讲述了测试驱动的编程为什么是一种好的编程方法。这里我将举一个简单的例子,让您体会一下我正在讲什么。

  假定我正在编写包含 Person 对象的系统。我希望在我问每个 Person 时,他/她能告诉我其年龄(作为整数)。即使我还没有编写一丁点代码,但也该编写测试了。“什么?”,您可能会说,“我甚至不知道在测试什么,怎么编写测试?”答案很简单,您 的确知道您在测试什么,只是不 知道您所了解的内容,因为您不习惯按这样的方式进行思考。这就是我的意思。

  您确实还没有任何代码,但您脑海中应有 Person 对象的雏形。 Person 对象上应该有一个方法,该方法可以用整数形式返回年龄。因为我最常使用 Java 语言,所以我用 JUnit 来编写程序员测试。清单 1 显示了我为 Person 对象编写的 JUnit 测试:


清单 1. 用于 Person 对象的 JUnit 测试

         

package com.roywmiller.testexample;
import junit.framework.TestCase;
public class TC_Person extends TestCase {
	protected Person person;
	public TC_Person(String name) {
		super(name);
	}
	protected void setUp() throws Exception {
		person = new Person();
	}
	public void testGetAge() {
		int actual = person.getAge();
		assertEquals(0, actual);
	}
	protected void tearDown() throws Exception {
	}
}


  首先,让我向那些不熟悉 JUnit 的人讲述一些浅显的原理。 TestCase 类是您将最常使用的类。您只是写了一个测试类(在该示例是 TC_Person ),它是 TestCase 的子类。(注:在 JUnit 3.8.1 中,可以有也可以没有接受 String 的构造函数,但由于我几乎所有的 Java 开发都在 Eclipse IDE(请参阅 参考资料)中完成,Eclipse IDE 免费向我提供了这个构造函数,所以我就把它保留在这里了。)一旦创建好测试类之后,测试方法中要有实际的动作。这些方法都恰如其分地用前缀 test 开头(它们必须是 public ,并且返回 void )。当运行测试时,JUnit:

  内省测试类,并执行每个以“test”开头的方法
  在执行每个测试方法之前执行 setUp() 方法
  在执行每个测试方法之后执行 tearDown() 方法
  在该示例中, setUp() 方法中没有太多要执行的语句。它只是实例化 Person (我用这个方法是让您觉得这个测试案例看上去很“完整”)。这意味着,如果这里有 20 个测试方法,则每个测试方法都以一个新的 Person 实例开始。 tearDown() 中不做任何事情,所以现在它是空的。值得强调的一点,您不需要 setUp() 或 tearDown() ;我通常直到编写第二个或第三个测试方法,并确定了这些方法都共享某些公共的设置或销毁活动时,才创建它们。

  有了这些原理之后,要注意,我在测试方法中制订了一些设计决策。我假定,可以构造一个 person,并且“缺省” Person 会返回值为 0 的 age。还假定 Person 对象有 getAge() 方法。即使那些假定不会一直都成立,但目前它们还适用。可以说,这是一个简单的测试,让我说明测试驱动的编程。有了这些假定之后,实例化 Person (在 setUp() 中实例化 Person 只是为了展示如何使用 setUp() 方法),接着调用测试方法中正在测试的方法,然后调用其中一种“断言(assert)”方法。断言方法测试事情是否为 true。换句话说,这些方法针对某件事做出一个断言,该断言告诉 JUnit 验证该事是否为 true。表 1 列出了断言的类别:

表 1. 断言类别

断言方法描述
assertEquals比较两件事物是否相等(基本类型或对象)
assertTrue对布尔值求值,看它是否为 true
assertFalse对布尔值求值,看它是否为 false
assertNull检查对象是否为 null
assertNotNull检查对象是否不为 null
assertSame检查两个对象是否为同一实例
assertNotSame检查两个对象是否不为同一实例

在这里,我检查Person实例的 age 是否为 0,新Person对象的缺省值为 0。

当然,这个测试甚至不能编译。图 1 显示了当我试图在 Eclipse 上运行它时的 JUnit Fast View。

41/41234>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

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

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

沪ICP备05003035号

沪公网安备 31010102002173号