测试驱动开发—测试驱动开发简介_第1页
测试驱动开发—测试驱动开发简介_第2页
测试驱动开发—测试驱动开发简介_第3页
测试驱动开发—测试驱动开发简介_第4页
测试驱动开发—测试驱动开发简介_第5页
已阅读5页,还剩52页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、 2002 IBM Corporation测试驱动开发1.1测试驱动开发简介Jan 2010 张文胜xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY2主题 测试驱动开发的基本概念 测试驱动的基本流程 测试驱动的所采用的技术及工具 DEMOxianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY3本章目标 掌握测试驱动开发的基本概念 掌握驱动的基本流程 掌握测试驱动的所采用的技术及工具xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY41-1 基本概念 为什么会出现测试驱动开发?l

2、 当有一个新的开发任务时,往往第一个念头就是如何去实现当有一个新的开发任务时,往往第一个念头就是如何去实现 它呢?它呢?l “应该是这么做的吧,嗯,差不多就是这样的应该是这么做的吧,嗯,差不多就是这样的” ” 。l 抓起任务就开始编码,一边写,一边修改和设计。抓起任务就开始编码,一边写,一边修改和设计。l 时间这么紧!我还是先实现任务吧,然后再好好测试。时间这么紧!我还是先实现任务吧,然后再好好测试。l 还是不工作,时间不多了。不管了,还是先做个实现,以后还是不工作,时间不多了。不管了,还是先做个实现,以后再来整理代码吧。再来整理代码吧。l 我已经单步调试了好几次了,遍历了所有可能的分支,应该

3、我已经单步调试了好几次了,遍历了所有可能的分支,应该不会有问题了,提交,今天可以好好休息一下了。不会有问题了,提交,今天可以好好休息一下了。l 要不要写单元测试把我刚才单步调试的步骤写下来啊?那样要不要写单元测试把我刚才单步调试的步骤写下来啊?那样是很好,但工作量很大哦。是很好,但工作量很大哦。l 这样的情况要作自动测试太复杂了。还是手工测试一下吧。这样的情况要作自动测试太复杂了。还是手工测试一下吧。l 程序员应该做些有创意的东西,这样才有趣啊。程序员应该做些有创意的东西,这样才有趣啊。l 测试是测试是QAQA的事,我为什么要做啊,我做了他们干什么啊。的事,我为什么要做啊,我做了他们干什么啊。

4、xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY51-1 基本概念 为什么会出现测试驱动开发?l 奇怪了,怎么代码跟开发文档上有这么大的差别啊。奇怪了,怎么代码跟开发文档上有这么大的差别啊。l 这段代码究竟想表达什么意思?这段代码究竟想表达什么意思? l 代码现在越来越乱了,我都不敢修改代码了,修改了这个地方,代码现在越来越乱了,我都不敢修改代码了,修改了这个地方,天晓得会引起多少别的地方出错啊天晓得会引起多少别的地方出错啊! !l 这个地方的代码怎么好象在那个地方看到过啊?这个程序里怎这个地方的代码怎么好象在那个地方看到过啊?这个程序里怎么会有这么多

5、的重复代码呢?么会有这么多的重复代码呢?l 开发部在干什么啊,开发部在干什么啊,BUGBUG怎么这么多,他们有没有自己先测试怎么这么多,他们有没有自己先测试一下啊。一下啊。l 这下好了,让他们修改了一个这下好了,让他们修改了一个BUGBUG,现在一下子来了这么多的,现在一下子来了这么多的BUGBUG。l他们到底在搞什么啊,有没有从用户的角度考虑啊,我新增一他们到底在搞什么啊,有没有从用户的角度考虑啊,我新增一个采购订单,订单项竟然可以输入负数。个采购订单,订单项竟然可以输入负数。 xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY61-1 基本概念有办法

6、可以解决上面的矛盾吗?xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY71-1 基本概念什么是测试驱动?测试驱动是一种开发形式:测试驱动是一种开发形式:1.1.首先要编写测试代码首先要编写测试代码2.2.除非存在相关测试,否则不编写任何的产品代码除非存在相关测试,否则不编写任何的产品代码3.3.由测试来决定需要编写什么样的代码由测试来决定需要编写什么样的代码4.4.要求维护一套详尽的测试集要求维护一套详尽的测试集xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY81-1 基本概念 测试驱动所要达到的目标clean

7、 code that workclean code that work 测试驱动所追求的目标就是代码整洁可用,其实测试驱动所追求的目标就是代码整洁可用,其实现的规则就是:现的规则就是:1.1.只有测试失败时,我们才写代码只有测试失败时,我们才写代码2.2.消除重复设计,优化设计结构消除重复设计,优化设计结构xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY91-2 基本流程 测试驱动开发的基本流程l定义应用程序的要求定义应用程序的要求l熟悉应用程序的功能区域,确定要使用的单项功能项或功能要求熟悉应用程序的功能区域,确定要使用的单项功能项或功能要求l创建验

8、证要求的测试列表创建验证要求的测试列表l为功能或要求定义接口和类为功能或要求定义接口和类l编写测试代码编写测试代码l运行测试运行测试l根据测试生成产品代码根据测试生成产品代码l重新运行测试,根据测试修改产品代码,直到所有测试都通过重新运行测试,根据测试修改产品代码,直到所有测试都通过l整理代码整理代码l重复上面的步骤重复上面的步骤xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY101-2 基本流程 测试驱动开发的基本流程xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY111-3 技术及工具 技术及工具介绍Ecl

9、ipseEclipse是一个集成开发工具的通用平台,因是一个集成开发工具的通用平台,因为任何人都可以免费获得源码以及软件本身为任何人都可以免费获得源码以及软件本身的方便,因此成为许多人的喜爱,的方便,因此成为许多人的喜爱,junitjunit是供是供JavaJava编码人员做单元测试之用,在编码人员做单元测试之用,在eclipseeclipse中中使用使用junitjunit测试测试JavaJava程序能够体现测试驱动开程序能够体现测试驱动开发的测试思想,本课通过实例对在发的测试思想,本课通过实例对在eclipseeclipse中中使用使用junitjunit测试的方法进行了分析。测试的方法进

10、行了分析。 xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY121-3 技术及工具 Eclipse介绍Eclipse Eclipse 是是IBMIBM投入巨资开发的替代投入巨资开发的替代IBM IBM Visual Age for Java Visual Age for Java 的下一代的下一代IDEIDE开发环境,在开开发环境,在开发结束以后发结束以后IBMIBM把该软件赠与了国际开放源代码团体,把该软件赠与了国际开放源代码团体,任何人都能轻易地下载到任何人都能轻易地下载到EclipseEclipse的源代码。软件可的源代码。软件可以集成很多软件开

11、发工具供应商的产品,任何开发工以集成很多软件开发工具供应商的产品,任何开发工具厂商都可以将开发工具或许主件加入到具厂商都可以将开发工具或许主件加入到EclipseEclipse平平台,用户可以通过相同的接口使用不同的工具,也就台,用户可以通过相同的接口使用不同的工具,也就是在一个集成环境中使用不同的开发工具。是在一个集成环境中使用不同的开发工具。 xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY131-3 技术及工具 JUnit介绍JUnitJUnit是一款由是一款由Erich GammaErich Gamma(设计模式设计模式的的作者)和作者)和Ke

12、nt BeckKent Beck(极限编程的提出者)编写的开(极限编程的提出者)编写的开源的回归测试框架,供源的回归测试框架,供JavaJava编码人员做单元测试之用,编码人员做单元测试之用,可以从网站上免费获得。可以从网站上免费获得。因为两种软件的源码都能从网上免费下载,因因为两种软件的源码都能从网上免费下载,因此受到越来越多的开发人员、测试人员的喜爱。此受到越来越多的开发人员、测试人员的喜爱。 xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY141-4 DEMO -一个简单的测试 TDDTDD意味着您不仅需要为每一段代码编写测试用例,而且意味着您不

13、仅需要为每一段代码编写测试用例,而且意味着测试优先。测试用例用来定义代码需要做什么。在意味着测试优先。测试用例用来定义代码需要做什么。在完成相应的代码之后,运行测试用例来保证代码确实符合完成相应的代码之后,运行测试用例来保证代码确实符合测试用例的规定。测试用例的规定。 创建一个简单的创建一个简单的JavaJava类类 创建一个测试类来执行这个创建一个测试类来执行这个JavaJava类类 使用使用JUnitJUnit框架框架 学习构造函数学习构造函数 重构您所写的代码重构您所写的代码xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY151-4 DEMO -

14、一个简单的测试 StudentTest StudentTest将创建将创建StudentStudent类的对象,发送消息给这些对象,类的对象,发送消息给这些对象,并且证明一旦所有的消息被发送出去,一切都能像预期的那样。并且证明一旦所有的消息被发送出去,一切都能像预期的那样。因而因而StudentTestStudentTest类依赖于类依赖于StudentStudent类,如图中的有向关联所表类,如图中的有向关联所表达的意思。相反,达的意思。相反,StudentStudent不依赖于不依赖于StudentTestStudentTest:生产类对为生产类对为它编写的测试一无所知。它编写的测试一无所

15、知。xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY161-4 DEMO -一个简单的测试 TDD意味着您不仅需要为每一段代码编写测试用例,而且意味着测试优先。 一、创建项目一、创建项目 启动启动EclipseEclipse,选择工作区,选择,选择工作区,选择filefile菜单下的菜单下的newnew创建创建project,project,选择类别为选择类别为Java projectJava project,我们为项目起名为,我们为项目起名为Drive_Test,Drive_Test,保存项目文件。保存项目文件。xianzws Jan 2010 20

16、10 XIAN EURASIA UNIVERSITY171-4 DEMO -一个简单的测试 二、写测试用例二、写测试用例获取学生信息是初始的需求。为了表达这个需求,开始获取学生信息是初始的需求。为了表达这个需求,开始创建一个用作测试用例的类。创建一个用作测试用例的类。首先,在您的机器上新建一个包,然后在这个包里面创首先,在您的机器上新建一个包,然后在这个包里面创建一个文件建一个文件StudentTest.javaStudentTest.java。在编辑器中输入代码:。在编辑器中输入代码: StudentTestStudentTest将从获得或是继承所有的将从获得或是继承所有的能力(行为)和数据

17、(属性能力(行为)和数据(属性) 必须指定该类为必须指定该类为public类型,这样类型,这样JUnit测试框架才能识别它测试框架才能识别它xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY181-4 DEMO -一个简单的测试 UML UML类图展示了类图展示了StudentTest StudentTest 和和 之间的继承关系。现在之间的继承关系。现在StudentTestStudentTest同时依赖于同时依赖于和和StudentStudent。请记住表示不同依赖关。请记住表示不同依赖关系的箭头有所区别:封闭的箭头表示继承关系。系的箭头有所区别:封

18、闭的箭头表示继承关系。 xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY191-4 DEMO -一个简单的测试 二、写测试用例二、写测试用例配置配置EclipseEclipse,在构建路径中添加,在构建路径中添加JUnitJUnit类库。在工具条类库。在工具条上点击项目上点击项目-属性,选择属性,选择JavaJava构建路径,库,选择添加构建路径,库,选择添加外部外部JARJAR,浏览,浏览JunitJunit被存储的目录,选择被存储的目录,选择junit.jarjunit.jar,点,点击打开。将会看见击打开。将会看见JUnitJUnit出现在库的列

19、表中。点击确定,出现在库的列表中。点击确定,让让EclipseEclipse重建路径。重建路径。 xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY201-4 DEMO -一个简单的测试 二、写测试用例二、写测试用例 也可以这样建立测试,在也可以这样建立测试,在Drive_TestDrive_Test的标题上面点击的标题上面点击右键,选择新建右键,选择新建-其他,展开其他,展开“Java”Java”选项,选择选项,选择JUnitJUnit。在右边的栏目对话框中选择测试案例,然后下一步。在右边的栏目对话框中选择测试案例,然后下一步。 xianzws Jan

20、 2010 2010 XIAN EURASIA UNIVERSITY211-4 DEMO -一个简单的测试 三、执行测试用例三、执行测试用例 当成功编译了当成功编译了StudentTest, StudentTest, 可以在可以在JUnitJUnit中执行中执行它。它。JUnitJUnit提供了两个提供了两个GUIGUI界面和一个文本界面。界面和一个文本界面。 点击点击Run- JUnit TestRun- JUnit Test按钮可以执行这个测试,按钮可以执行这个测试,会看到一根红条从窗口的一边快速到达另一边。会看到一根红条从窗口的一边快速到达另一边。 实际上,实际上,JUnitJUnit的

21、红条表示发生了错误。红条下方的的红条表示发生了错误。红条下方的摘要说明有一个失败。摘要说明有一个失败。“Erros and Failures”Erros and Failures”列表解释列表解释了发生的错误:在这个例子中,了发生的错误:在这个例子中,JUnitJUnit抱怨抱怨“在在StudentTestStudentTest中没有任何测试中没有任何测试”。所以作为。所以作为TDDTDD程序员,首要任务是检查程序员,首要任务是检查JUnitJUnit中的错误,然后快速更正它们。中的错误,然后快速更正它们。xianzws Jan 2010 2010 XIAN EURASIA UNIVERSIT

22、Y221-4 DEMO -一个简单的测试 四、增加一个测试四、增加一个测试 编辑编辑StudentTestStudentTest类,增加一个测试,如下:类,增加一个测试,如下: public void testCreate() xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY231-4 DEMO -一个简单的测试 四、增加一个测试四、增加一个测试 方法方法testCreate testCreate 没有返回任何值给没有返回任何值给JUnitJUnit,当,当然然JUnitJUnit也不需要这样的信息。一个方法不返回任何也不需要这样的信息。一个方法不返回

23、任何信息,那么它的返回值为空类型。信息,那么它的返回值为空类型。 方法名方法名testCreatetestCreate,暗示这是一个测试方法。对,暗示这是一个测试方法。对JavaJava而言,而言,这不过是个方法名。但是这不过是个方法名。但是JUnitJUnit根据名称来识别一个测试方根据名称来识别一个测试方法,所以测试方法的命名要遵从下面的标准:法,所以测试方法的命名要遵从下面的标准: 方法必须声明为方法必须声明为publicpublic 方法的返回值必须为方法的返回值必须为voidvoid 方法的名字必须以小写方法的名字必须以小写testtest为前缀为前缀 方法不能接受任何参数方法不能接

24、受任何参数xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY241-4 DEMO -一个简单的测试 五、编译您的代码,并且重新运行五、编译您的代码,并且重新运行JUnitJUnit。 JUnit JUnit执行成功(显示一根绿条),对测试类执行成功(显示一根绿条),对测试类StudentTestStudentTest,JUnitJUnit显示成功地执行了一个测试方显示成功地执行了一个测试方法,没有任何错误和失败。法,没有任何错误和失败。 请记住在请记住在testCreatetestCreate中没有任何代码。中没有任何代码。JUnitJUnit执执行成功

25、表明空的测试方法一定可以通过。行成功表明空的测试方法一定可以通过。xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY251-4 DEMO -一个简单的测试 六、创建六、创建 StudentStudent对象对象 在在testCreatetestCreate方法中增加一行代码:方法中增加一行代码: public void testCreate() new Student(Jane Doe); 当测试框架调用当测试框架调用testCreatetestCreate方法时,方法时,JavaJava会执行增加的这会执行增加的这行语句。一旦行语句。一旦JavaJav

26、a执行完这行语句,就把控制权交回给调用执行完这行语句,就把控制权交回给调用testCreatetestCreate的测试框架。的测试框架。 testCreate testCreate方法中新增的这行语句告诉方法中新增的这行语句告诉JavaJava去创建去创建StudentStudent类的对象。类的对象。 把关键字把关键字newnew放在类名的前面,类名的后面是参数列表。放在类名的前面,类名的后面是参数列表。参数列表包含了为了实例化一个参数列表包含了为了实例化一个StudentStudent对象所要的信息。当对象所要的信息。当JavaJava虚拟机执行到虚拟机执行到newnew操作符时,操作符

27、时,JavaJava虚拟机分配一块内存来虚拟机分配一块内存来存储这个存储这个StudentStudent对象。对象。JavaJava虚拟机根据虚拟机根据StudentStudent类的定义来决类的定义来决定内存分配的大小。定内存分配的大小。 xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY261-4 DEMO -一个简单的测试 七、创建七、创建 StudentStudent类类 编译您的代码,并且重新运行编译您的代码,并且重新运行JUnitJUnit。因为只编写。因为只编写了测试类了测试类StudentTestStudentTest,所以会发现有错误。

28、,所以会发现有错误。StudentTestStudentTest引用了引用了StudentStudent类,然而还没有创建后者。类,然而还没有创建后者。 我们期望编译错误。编译时发现错误可以在整个开我们期望编译错误。编译时发现错误可以在整个开发过程中向我们提供反馈,这是好事情。您可以把编发过程中向我们提供反馈,这是好事情。您可以把编译错误看作编写测试后,得到的第一个反馈:您编写代译错误看作编写测试后,得到的第一个反馈:您编写代码是否用了正确的码是否用了正确的JavaJava语法,使得测试可以被正确执行?语法,使得测试可以被正确执行? xianzws Jan 2010 2010 XIAN EUR

29、ASIA UNIVERSITY271-4 DEMO -一个简单的测试 七、创建七、创建 StudentStudent类类 xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY281-4 DEMO -一个简单的测试 七、创建七、创建 StudentStudent类类 为了消除目前的错误。我们创建一个新类为了消除目前的错误。我们创建一个新类Student.javaStudent.java。输入下面的代码:输入下面的代码: class Student xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY291-4 DEMO

30、-一个简单的测试 七、创建构造函数七、创建构造函数 您会看到一个新的类似的报错。编译器再一次没能找到一个符号,您会看到一个新的类似的报错。编译器再一次没能找到一个符号,但这一次指到了关键字但这一次指到了关键字newnew。同时,编译器指出该符号寻找一个以。同时,编译器指出该符号寻找一个以StringString为参数的构造函数。为参数的构造函数。 现在构造函数对传入的姓名字符串不作任何事情。传入的字符串好像现在构造函数对传入的姓名字符串不作任何事情。传入的字符串好像消失在了大气中。消失在了大气中。 class Student Student(String name) 类名作为构造函数的名字类名

31、作为构造函数的名字 ,不能从构不能从构造函数返回值,甚至不能返回空值。造函数返回值,甚至不能返回空值。 xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY301-4 DEMO -一个简单的测试 八、局部变量八、局部变量 操作符操作符newnew返回对象在内存的地址的引用,可以通过返回对象在内存的地址的引用,可以通过赋值操作符(赋值操作符(= =)来存储如下引用。修改)来存储如下引用。修改StudentTestStudentTest:创建了一个名字叫创建了一个名字叫studentstudent的的StudentStudent类型的引用。类型的引用。 pub

32、lic void testCreate() Student student = new Student(Jane Doe); xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY311-4 DEMO -一个简单的测试 八、从方法返回一个值八、从方法返回一个值 向测试中创建的向测试中创建的StudentStudent对象请求学生的姓名。消息发送的对象请求学生的姓名。消息发送的名字是名字是getNamegetName,并且这个消息不需要任何额外信息(参数)。,并且这个消息不需要任何额外信息(参数)。 public void testCreate() Stude

33、nt student = new Student(Jane Doe);String studentName = student.getName(); xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY321-4 DEMO -一个简单的测试 九、九、StudentStudent类定义中添加类定义中添加getNamegetName方法方法 编译所有的源文件。编译错误表明编译器无法找到编译所有的源文件。编译错误表明编译器无法找到StudentStudent类中的类中的getNamegetName方法。往方法。往StudentStudent类定义中添加类定义中添

34、加getNamegetName方法,方法,就可以消除这个错误。就可以消除这个错误。 class Student public Student(String name) public String getName() return ; return return语句返回了一个空语句返回了一个空StringString对象。对象。再次编译,就不会有任何编译错误了。可以再次编译,就不会有任何编译错误了。可以再次用再次用JUnitJUnit运行这个测试。运行这个测试。 xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY331-4 DEMO -一个简单的测试 十、

35、断言十、断言 现在,现在,testCreatetestCreate有了完整的内容:第一行测试语句用指定的姓名创有了完整的内容:第一行测试语句用指定的姓名创建了一个建了一个StudentStudent对象,第二行语句从这个对象,第二行语句从这个StudentStudent对象请求获得姓名。现在对象请求获得姓名。现在您需要做的就是证明学生姓名和预期的一样,也就是说学生姓名和通过构造您需要做的就是证明学生姓名和预期的一样,也就是说学生姓名和通过构造函数传递给函数传递给StudentStudent对象的姓名一样。对象的姓名一样。 public void testCreate() Student stu

36、dent = new Student(Jane Doe);String studentName = student.getName(); assertEquals(Jane Doe, studentName); 是一个断言,断言的第一个参数要和第是一个断言,断言的第一个参数要和第二个参数相同。二个参数相同。 xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY341-4 DEMO -一个简单的测试 十、断言十、断言 重新编译并运行测试,重新编译并运行测试,JUnitJUnit显示一根红条,表示有一个错误。并且显示一根红条,表示有一个错误。并且在第一个列表框

37、中告诉您出错原因:在第一个列表框中告诉您出错原因:Failure: testCreate(StudentTest): expected: but was:Failure: testCreate(StudentTest): expected: but was:。 xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY351-4 DEMO -一个简单的测试 十、断言十、断言 assertEqualsassertEquals方法比较两个对象,如果不相同就返回失败。方法比较两个对象,如果不相同就返回失败。JUnitJUnit将第将第一个参数一个参数“Jane Doe

38、”Jane Doe”视为预期值。第二个参数视为预期值。第二个参数studentNamestudentName变量,作为实际值。变量,作为实际值。您期望实际值也是您期望实际值也是“Jane Doe”Jane Doe”,然而,然而studentNamestudentName是空字符串,因为您从是空字符串,因为您从getNamegetName方法中返回一个空字符串。方法中返回一个空字符串。class Student public Student(String name) public String getName() return Jane Doe ; 修改代码非常简单,改变修改代码非常简单,改变g

39、etNamegetName方法,方法,返回字符串返回字符串“Jane Doe” Jane Doe” xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY361-4 DEMO -一个简单的测试 十、实例变量十、实例变量 您已经编写了第一个测试和相应的类。使用您已经编写了第一个测试和相应的类。使用StudentTestStudentTest来帮助来帮助您以一种渐增的方式来构建您以一种渐增的方式来构建StudentStudent类。该测试也用来保证未来的任何改类。该测试也用来保证未来的任何改变不会影响已经完成的代码。变不会影响已经完成的代码。不幸的是,前面的代码

40、并不十分完善。假如您创建了更多的学生对不幸的是,前面的代码并不十分完善。假如您创建了更多的学生对象,所有的学生对象都会说自己名字叫象,所有的学生对象都会说自己名字叫“Jane Doe”Jane Doe”,来响应,来响应getNamegetName消息。消息。通过解决这个问题,您将使通过解决这个问题,您将使StudentTestStudentTest类和类和StudentStudent类趋于成熟。类趋于成熟。您可以证明:每个您可以证明:每个StudentStudent对象会通过对象会通过testCreatetestCreate方法,并返回自己的名字方法,并返回自己的名字“Jane Doe”“Ja

41、ne Doe”。添加代码创建第二个。添加代码创建第二个studentstudent对象:对象:xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY371-4 DEMO -一个简单的测试 十、实例变量十、实例变量 public void testCreate() Student student = new Student(Jane Doe); String studentName = student.getName(); assertEquals(Jane Doe, studentName); Student secondStudent = new Stud

42、ent(Joe Blow); String secondStudentName = secondStudent.getName(); assertEquals(Joe Blow, secondStudentName); xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY381-4 DEMO -一个简单的测试 十、实例变量十、实例变量 很明显,由于很明显,由于getNamegetName方法总是返回方法总是返回“Jane Doe”Jane Doe”,所以第二个,所以第二个assertEqualsassertEquals语句会使测试失败。语句会使测试失败。

43、问题是把学生姓名传给问题是把学生姓名传给StudentStudent类的构造函数,但是构造函数对姓名类的构造函数,但是构造函数对姓名不作任何处理。如果后面打算使用姓名,不作任何处理。如果后面打算使用姓名,StudentStudent类要负责存储姓名。类要负责存储姓名。 需要把学生姓名作为需要把学生姓名作为studentstudent的一个属性,这是一个被的一个属性,这是一个被studentstudent对象保对象保存的信息。存的信息。JavaJava中表现属性最直接的方式是将其定义为成员变量,也叫实例中表现属性最直接的方式是将其定义为成员变量,也叫实例变量。在表示类的开始和结束的花括号对之间来

44、定义成员变量。成员变量可变量。在表示类的开始和结束的花括号对之间来定义成员变量。成员变量可以出现在方法以外的类定义的任何地方。不过根据约定,您最好把成员变量以出现在方法以外的类定义的任何地方。不过根据约定,您最好把成员变量放在类的开始或者结束的地方。放在类的开始或者结束的地方。 就像局部变量,成员变量也有类型。这里定义了就像局部变量,成员变量也有类型。这里定义了StringString类型的成员变量类型的成员变量 myNamemyName。 xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY391-4 DEMO -一个简单的测试 十、实例变量十、实例变量

45、 class Student class Student String myName;String myName;public Student(String name) public Student(String name) myName = name;myName = name; public String getName() public String getName() return return myName;myName; 定义定义StringString类型的成员变量类型的成员变量 myName myName 在构造函数中,把参数在构造函数中,把参数namename赋值赋值给给my

46、Name myName 从从getNamegetName方法中返回方法中返回myNamemyName,而不是返回字符串而不是返回字符串“Jane Doe” Jane Doe” xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY401-4 DEMO -一个简单的测试 十、实例变量十、实例变量 为了进一步支持断言,在测试方法的最后增加一行代码,从而确保您可以正确获取第一个为了进一步支持断言,在测试方法的最后增加一行代码,从而确保您可以正确获取第一个studentstudent对象的姓名。测试成功表明对象的姓名。测试成功表明studentstudent和和se

47、condStudentsecondStudent指向两个不同的对象。指向两个不同的对象。 public void testCreate() Student student = new Student(Jane Doe); String studentName = student.getName(); assertEquals(Jane Doe, studentName); Student secondStudent = new Student(Joe Blow); String secondStudentName = secondStudent.getName(); assertEquals(

48、Joe Blow, secondStudentName);assertEquals(Jane Doe, student.getName(); 注意,这里没有把注意,这里没有把student.getName()student.getName()的返的返回值赋值给一个局部变量,而是直接把回值赋值给一个局部变量,而是直接把student.getName()student.getName()作为了作为了assertEqualsassertEquals的第二的第二个参数。个参数。 xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY411-4 DEMO -一个简单的

49、测试 总结总结 在良好设计的面向对象在良好设计的面向对象JavaJava编码中,大多数语句是创建新的编码中,大多数语句是创建新的对象、发送消息给其它对象、或者将对象地址赋值(用对象、发送消息给其它对象、或者将对象地址赋值(用newnew创建创建对象,或者从消息发送返回对象)给对象引用。对象,或者从消息发送返回对象)给对象引用。xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY421-4 DEMO -一个简单的测试 十一、重构十一、重构 软件开发中的一个主要问题是代码维护的高成本。原因之一是匆忙行动软件开发中的一个主要问题是代码维护的高成本。原因之一是匆忙

50、行动或者纯粹疏忽导致的代码混乱。软件开发的主要任务是让软件可以工作,或者纯粹疏忽导致的代码混乱。软件开发的主要任务是让软件可以工作,可以通过在编码之前先编写测试代码来应对这个挑战。其次,您的工作要可以通过在编码之前先编写测试代码来应对这个挑战。其次,您的工作要确保代码是干净的。可以通过两种机制来实现:确保代码是干净的。可以通过两种机制来实现:1 1保证在系统中没有重复的代码。保证在系统中没有重复的代码。2 2保证代码是干净的,并且富有表现力,可以清晰地体现程序员的意图。保证代码是干净的,并且富有表现力,可以清晰地体现程序员的意图。 贯穿敏捷贯穿敏捷JavaJava的进程,经常停下来反思刚刚写下

51、的代码。任何不符合的进程,经常停下来反思刚刚写下的代码。任何不符合这两条简单准则的代码都需要立刻重新处理,或者重构。即使设计非常完这两条简单准则的代码都需要立刻重新处理,或者重构。即使设计非常完美,糟糕的代码实现同样会给修改它带来非常头痛的体验。美,糟糕的代码实现同样会给修改它带来非常头痛的体验。 在您前进的时候,越是持续雕琢改进您的代码,您遇到需要付出高昂代在您前进的时候,越是持续雕琢改进您的代码,您遇到需要付出高昂代价才能解决代码错误的可能性就越小。原则是永远不能让代码比开始时的价才能解决代码错误的可能性就越小。原则是永远不能让代码比开始时的状况要差。状况要差。 xianzws Jan 2

52、010 2010 XIAN EURASIA UNIVERSITY431-4 DEMO -一个简单的测试 十一、重构十一、重构 第一步要清除不必要的局部变量:第一步要清除不必要的局部变量:studentNamestudentName和和secondStudentNamesecondStudentName。它们丝毫无助于对方法的理解,它们可以被它们丝毫无助于对方法的理解,它们可以被studentstudent对象的查询所替代,对象的查询所替代,就像最后一个就像最后一个assertEqualsassertEquals。 当您完成这样的修改后,应重新编译和在当您完成这样的修改后,应重新编译和在JUni

53、tJUnit中运行测试,以确保中运行测试,以确保没有不好的影响。您的代码看起来像这样:没有不好的影响。您的代码看起来像这样:xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY441-4 DEMO -一个简单的测试 十一、重构十一、重构-第一步第一步 public void testCreate() Student student = new Student(Jane Doe); assertEquals(Jane Doe, student.getName() ); Student secondStudent = new Student(Joe Blow)

54、; assertEquals(Joe Blow, secondStudent.getName(); xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY451-4 DEMO -一个简单的测试 十一、重构十一、重构 第二步:代码中到处嵌入字符串被视作不良的编程习惯。一个原因是,如果每第二步:代码中到处嵌入字符串被视作不良的编程习惯。一个原因是,如果每个字符串所代表的意义不清晰的话,将很难理解这样的代码。个字符串所代表的意义不清晰的话,将很难理解这样的代码。 在这个例子中,您违背了不能有重复代码的准则。每个字符串都出现了两次。在这个例子中,您违背了不能有重复代

55、码的准则。每个字符串都出现了两次。如果您不得不改变其中之一的话,您将不得不改变另一个。这样工作量就更多了。如果您不得不改变其中之一的话,您将不得不改变另一个。这样工作量就更多了。而且意味这样的可能性:改变了一个,没有改变另一个,从而代码中引入了缺陷。而且意味这样的可能性:改变了一个,没有改变另一个,从而代码中引入了缺陷。 消除此类冗余的方法是用字符串常量来替代一个字符串。消除此类冗余的方法是用字符串常量来替代一个字符串。 final String firstStudentName = Jane Doe; final String firstStudentName = Jane Doe; 这条语

56、句创建了这条语句创建了StringString类型类型firstStudentNamefirstStudentName,并赋给其初始值,并赋给其初始值“Jane Doe”Jane Doe”。语句开头的关键字语句开头的关键字finalfinal,表明这个字符串引用是不可修改的,其它对象不可赋值给这,表明这个字符串引用是不可修改的,其它对象不可赋值给这个引用。您从来没有被要求指定个引用。您从来没有被要求指定finalfinal,但这被认为是一种好的形式,可以帮助记住,但这被认为是一种好的形式,可以帮助记住firstStudentNamefirstStudentName将像常量一样去工作。将像常量一

57、样去工作。 xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY461-4 DEMO -一个简单的测试 十一、重构十一、重构-第二步第二步 public void testCreate() final String firstStudentName = Jane Doe; Student firstStudent = new Student(firstStudentName); assertEquals(firstStudentName, firstStudent.getName(); final String secondStudentName = Jo

58、e Blow; Student secondStudent = new Student(secondStudentName); assertEquals(secondStudentName, secondStudent.getName(); xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY471-4 DEMO -一个简单的测试 十一、重构十一、重构 您的开发循环是:您的开发循环是: 编写一个小的测试,来断言某些功能正确与否。编写一个小的测试,来断言某些功能正确与否。 运行测试,如果结果是失败。运行测试,如果结果是失败。 编写代码,使测试通过。编写代码,

59、使测试通过。 重构测试和代码,清除重复的概念,确保代码富于表现力。重构测试和代码,清除重复的概念,确保代码富于表现力。这样的循环,会很快成为一种根深蒂固、自然的开发流程。这样的循环,会很快成为一种根深蒂固、自然的开发流程。xianzws Jan 2010 2010 XIAN EURASIA UNIVERSITY481-4 DEMO -一个简单的测试 十二、十二、thisthis 审视审视StudentStudent类的代码,看看是否有可以改进的地方。类的代码,看看是否有可以改进的地方。 代码看起来干净,但是成员变量代码看起来干净,但是成员变量myNamemyName的命名过于学生气。用一个更好的名字的命名过于学生气。用一个更好的名字来体现您的专业素养。第一个想法可能是把成员变量命名为来体现您的专业素养。第一个想法可能是把成员变量命名为studentNamestudentName。但是,。但是,这样将导致命名重复,因为该成员变量定义在这样将导致命名重复,因为该成员变量定义在StudentStudent类中,所以很清楚这个成员类中,所以很清楚这个成员变量表示的就是变量表示的就是studentstudent的姓名。的姓名。 而且,您还需要重新命名针对这个成员变量的而且,您还需要重新命名针对这个成

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论