2022年junit单元测试教程教程 .pdf
《2022年junit单元测试教程教程 .pdf》由会员分享,可在线阅读,更多相关《2022年junit单元测试教程教程 .pdf(9页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、测试的概念长期以来,我所接触的软件开发人员很少有人能在开发的过程中进行测试工作。大部分的项目都是在最终验收的时候编写测试文档。有些项目甚至没有测试文档。现在情况有了改变。我们一直提倡UML、RUP、软件工程、CMM,目的只有一个,提高软件编写的质量。举一个极端的例子:如果你是一个超级程序设计师,一个传奇般的人物。(你可以一边喝咖啡,一边听着音乐,同时编写这操作系统中关于进程调度的模块,而且两天时间内就完成了!)我真得承认,有这样的人。(那个编写UNIX 中的 vi 编辑器的家伙就是这种人。)然而非常遗憾的是这些神仙们并没有留下如何修成正果的README。所以我们这些凡人在同一时间只能将注意力集
2、中到若干点(据科学统计,我并不太相信,一般的人只能同时考虑最多7个左右的问题,高手可以达到12 个左右),而不能既纵览全局又了解细节只能期望于其他的方式来保证我们所编写的软件质量。为了说明我们这些凡人是如何的笨。有一个聪明人提出了软件熵(software entropy)的概念:一个程序从设计很好的状态开始,随着新的功能不断地加入,程序逐渐地失去了原有的结构,最终变成了一团乱麻。你可能会争辩,在这个例子中,设计很好的状态实际上并不好,如果好的话,就不会发生你所说的情况。是的,看来你变聪明了,可惜你还应该注意到两个问题:1)我们不能指望在恐龙纪元(大概是十年前)设计的结构到了现在也能适用吧。2)
3、拥有签字权的客户代表可不理会加入一个新功能是否会对软件的结构有什么影响,即便有影响也是程序设计人员需要考虑的问题。如果你拒绝加入这个你认为致命的新功能,那么你很可能就失去了你的住房贷款和面包(对中国工程师来说也许是米饭或面条,要看你是南方人还是北方人)。另外,需要说明的是我看过的一些讲解测试的书都没有我写的这么有人情味(不好意思.)。我希望看到这片文章的兄弟姐妹能很容易地接受测试的概念,并付诸实施。所以有些地方写的有些夸张,欢迎对测试有深入理解的兄弟姐妹能体察民情,并不吝赐教。好了,我们现在言归正传。要测试,就要明白测试的目的。我认为测试的目的很简单也极具吸引力:写出高质量的软件并解决软件熵这
4、一问题。想象一下,如果你写的软件和Richard Stallman(GNU、FSF 的头儿)写的一样有水准的话,是不是很有成就感?如果你一致保持这种高水准,我保证你的薪水也会有所变动。测试也分类,白箱测试、黑箱测试、单元测试、集成测试、功能测试.。我们先不管有多少分类,如何分类。先看那些对我们有用的分类,关于其他的测试,有兴趣的人可参阅其他资料。白箱测试是指在知道被测试的软件如何(How)完成功能和完成什么样(What)的功能的条件下所作的测试。一般是由开发人员完成。因为开发人员最了解自己编写的软件。本文也是以白箱测试为主。黑箱测试则是指在知道被测试的软件完成什么样(What)的功能的条件下所
5、作的测试。一般是由测试人员完成。黑箱测试不是我们的重点。本文主要集中在单元测试上,单元测试是一种白箱测试。目的是验证一个或若干个类是否按所设计的那样正常工作。集成测试则是验证所有的类是否能互相配合,协同完成特定的任务,目前我们暂不关心它。下面我所提到的测试,除非特别说明,一般都是指单元测试。需要强调的是:测试是一个持续的过程。也就是说测试贯穿与开发的整个过程中,单元测试尤其适合于迭代增量式(iterative and incremental)的开发过程。Martin Fowler(有点儿像引用孔夫子的话)甚至认为:“在你不知道如何测试代码之前,就不应该编写程序。而一旦你完成了程序,测试代码也应
6、该完成。除非测试成功,你不能认为你编写出了可以工作的程序。”我并不指望所有的开发人员都能有如此高的觉悟,这种层次也不是一蹴而就的。但我们一旦了解测试的目的和好处,自然会坚持在开发过程中引入测试。因为我们是测试新手,我们也不理会那些复杂的测试原理,先说一说最简单的:测试就是比较预期的结果是否与实际执行的结果一致。如果一致则通过,否则失败。看下面的例子:名师资料总结-精品资料欢迎下载-名师精心整理-第 1 页,共 9 页 -/将要被测试的类public class Car public int getWheels()return 4;/执行测试的类public class testCar publ
7、ic static void main(String args)testCar myTest=new testCar();myTest.testGetWheels();public testGetWheels()int expectedWheels=4;Car myCar=Car();if(expectedWheels=myCar.getWheels()System.out.println(test Car:getWheels works perfected!);else System.out.println(test Car:getWheels DOESNT work!);如果你立即动手写了
8、上面的代码,你会发现两个问题,第一,如果你要执行测试的类testCar,你必须必须手工敲入如下命令:Windows d:java testCar Unix java testCar 即便测试如例示的那样简单,你也有可能不愿在每次测试的时候都敲入上面的命令,而希望在某个集成环境中(IDE)点击一下鼠标就能执行测试。后面的章节会介绍到这些问题。第二,如果没有一定的规范,测试类的编写将会成为另一个需要定义的标准。没有人希望查看别人是如何设计测试类的。如果每个人都有不同的设计测试类的方法,光维护被测试的类就够烦了,谁还顾得上维护测试类?另外有一点我不想提,但是这个问题太明显了,测试类的代码多于被测试的
9、类!这是否意味这双倍的工作?不!1)不论被测试类Car 的 getWheels 方法如何复杂,测试类testCar 的 testGetWheels 方法只会保持一样的代码量。2)提高软件的质量并解决软件熵这一问题并不是没有代价的。testCar 就是代价。我们目前所能做的就是尽量降低所付出的代价:我们编写的测试代码要能被维护人员容易的读取,我们编写测试代码要有一定的规范。最好IDE 工具可以支持这些规范。好了,你所需要的就是JUnit。一个 Open Source 的项目。用其主页上的话来说就是:“JUnit 是由 Erich Gamma 和 Kent Beck 编写的一个回归测试框架(reg
10、ression testing work)。用于 Java开发人员编写单元测试之用。”所谓框架就是Erich Gamma 和 Kent Beck 定下了一些条条框框,你编写的测试代码必须遵循这个条条框框:继承某个类,实现某个接口。其实也就是我们前面所说的规范。好在JUnit 目前得到了大多数软件工程师的认可。遵循JUnit 我们会得到很多的支持。回归测试就是你不断地对所编写的代码进行测试:编写一些,测试一些,调试一些,然后循环这一过程,你会不断地重复先前的测试,哪怕你正编写其他的类,由于软件熵的存在,你可能在编写第五个类的时候发现,第五个类的某个操作会导致第二个类的测试失败。名师资料总结-精品
11、资料欢迎下载-名师精心整理-第 2 页,共 9 页 -通过回归测试我们抓住了这条大Bug。回归测试框架-JUnit 通过前面的介绍,我们对JUnit 有了一个大概的轮廓。知道了它是干什么的。现在让我们动手改写上面的测试类testCar 使其符合 Junit 的规范能在JUnit 中运行。/执行测试的类(JUnit 版)import junit.work.*;public class testCar extends TestCase protected int expectedWheels;protected Car myCar;public testCar(String name)super(
12、name);protected void setUp()expectedWheels=4;myCar=new Car();public static Test suite()/*the type safe way*TestSuite suite=new TestSuite();suite.addTest(new testCar(Car.getWheels)protected void runTest()testGetWheels(););return suite;*/*the dynamic way*/return new TestSuite(testCar.class);public voi
13、d testGetWheels()assertEquals(expectedWheels,myCar.getWheels();改版后的testCar 已经面目全非。先让我们了解这些改动都是什么含义,再看如何执行这个测试。1import 语句,引入JUnit 的类。(没问题吧)2继承TestCase。可以暂时将一个TestCase看作是对某个类进行测试的方法的集合。详细介绍请参看JUnit 资料3setUp()设定了进行初始化的任务。我们以后会看到setUp 会有特别的用处。名师资料总结-精品资料欢迎下载-名师精心整理-第 3 页,共 9 页 -4testGetWheeels()对预期的值和my
14、Car.getWheels()返回的值进行比较,并打印比较的结果。assertEquals是 junit.work.Assert 中所定义的方法,junit.work.TestCase 继承了 junit.work.Assert。5suite()是一个很特殊的静态方法。JUnit 的 TestRunner 会调用 suite 方法来确定有多少个测试可以执行。上面的例子显示了两种方法:静态的方法是构造一个内部类,并利用构造函数给该测试命名(test name,如 Car.getWheels),其覆盖的runTest()方法,指明了该测试需要执行那些方法testGetWheels()。动态的方法是
15、利用内省(reflection)来实现 runTest(),找出需要执行那些测试。此时测试的名字即是测试方法(test method,如 testGetWheels)的名字。JUnit 会自动找出并调用该类的测试方法。6将 TestSuite看作是包裹测试的一个容器。如果将测试比作叶子节点的话,TestSuite就是分支节点。实际上TestCase,TestSuite以及 TestSuite组成了一个composite Pattern。JUnit 的文档中有一篇专门讲解如何使用Pattern 构造 Junit 框架。有兴趣的朋友可以查看JUnit 资料。如何运行该测试呢?手工的方法是键入如下命
16、令:Windows d:java junit.textui.TestRunner testCar Unix java junit.textui.TestRunner testCar 别担心你要敲的字符量,以后在IDE中,只要点几下鼠标就成了。运行结果应该如下所示,表明执行了一个测试,并通过了测试:.Time:0 OK(1 tests)如果我们将Car.getWheels()中返回的的值修改为3,模拟出错的情形,则会得到如下结果:.F Time:0 There was 1 failure:1)testGetWheels(testCar)junit.work.AssertionFailedErro
17、r:expected:but was:at testCar.testGetWheels(testCar.java:37)FAILURES!Tests run:1,Failures:1,Errors:0 注意:Time 上的小点表示测试个数,如果测试通过则显示OK。否则在小点的后边标上F,表示该测试失败。注意,在模拟出错的测试中,我们会得到详细的测试报告“expected:but was:”,这足以告诉我们问题发生在何处。下面就是你调试,测试,调试,测试.的过程,直至得到期望的结果。Design by Contract(这句话我没法翻译)Design by Contract 本是 Bertran
18、d Meyer(Eiffel 语言的创始人)开发的一种设计技术。我发现在 JUnit 中使用 Design by Contract 会带来意想不到的效果。Design by Contract 的核心是断言(assersion)。断言是一个布尔语句,该语句不能为假,如果为假,则表明出现了一个bug。Design by Contract 使用三种断言:前置条件(pre-conditions)、后置条件(post-conditions)和不变式(invariants)这里不打算详细讨论Design by Contract 的细节,而是希望其在测试中能发挥其作用。前置条件在执行测试之前可以用于判断是否
19、允许进入测试,即进入测试的条件。如expectedWheels 0,myCar!=null。后置条件用于在测试执行后判断测试的结果是否正确。如expectedWheels=myCar.getWheels()。而 不 变 式 在 判 断 交 易(Transaction)的 一 致 性(consistency)方面尤为有用。我希望JUnit 可以将 Design by Contract 作为未来版本的一个增强。名师资料总结-精品资料欢迎下载-名师精心整理-第 4 页,共 9 页 -Refactoring(这句话我依然没法翻译)Refactoring 本来与测试没有直接的联系,而是与软件熵有关,但既
20、然我们说测试能解决软件熵问题,我们也就必须说出解决之道。(仅仅进行测试只能发现软件熵,Refactoring 则可解决软件熵带来的问题。)软件熵引出了一个问题:是否需要重新设计整个软件的结构?理论上应该如此,但现实不允许我们这么做。这或者是由于时间的原因,或者是由于费用的原因。重新设计整个软件的结构会给我们带来短期的痛苦。而不停地给软件打补丁甚至是补丁的补丁则会给我们带来长期的痛苦。(不管怎样,我们总处于水深火热之中)Refactoring 是一个术语,用于描述一种技术,利用这种技术我们可以免于重构整个软件所带来的短期痛苦。当你refactor 时,你并不改变程序的功能,而是改变程序内部的结构
21、,使其更易理解和使用。如:该变一个方法的名字,将一个成员变量从一个类移到另一个类,将两个类似方法抽象到父类中。所作的每一个步都很小,然而12 个小时的Refactoring 工作可以使你的程序结构更适合目前的情况。Refactoring 有一些规则:1 不要在加入新功能的同时refactor 已有的代码。在这两者间要有一个清晰的界限。如每天早上 1-2 个小时的Refactoring,其余时间添加新的功能。2 在你开始Refactoring 前,和 Refactoring 后都要保证测试能顺利通过。否则 Refactoring 没有任何意义。3 进行小的Refactoring,大的就不是Ref
22、actoring 了。如果你打算重构整个软件,就没有必要 Refactoring 了。只有在添加新功能和调试bug 时才又必要Refactoring。不要等到交付软件的最后关头才Refactoring。那样和打补丁的区别不大。Refactoring 用在回归测试中也能显示其威力。要明白,我不反对打补丁,但要记住打补丁是应该最后使用的必杀绝招。(打补丁也需要很高的技术,详情参看微软网站)IDE对 JUnit 的支持目前支持JUnit 的 Java IDE 包括IDE 方式 个人评价(15,满分 5)Forte for Java 3.0 Enterprise Edition plug-in 3 J
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 2022年junit单元测试教程教程 2022 junit 单元测试 教程
限制150内