单元测试是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为。例如,你可能把一个很大的值放入一个有序list中去,然后确认该值出现在list的尾部。或者,你可能会从字符串中删除匹配某种模式的字符,然后确认字符串确实不再包含这些字符了。
执行单元测试,是为了证明某段代码的行为确实和开发者所期望的一致。
对于客户或最终使用者而言,这种测试必要吗,它与验收测试有关吗?这个问题仍然很难回答。事实上,我们在此并不关心整个产品的确认、验证和正确性等等;甚至此时,我们都不去关心性能方面的问题。我们所要做的一切就是要证明代码的行为和我们的期望一致。因此,我们所要测试的是规模很小的、非常独立的功能片断。通过对所有单独部分的行为建立起信心,确信它们都和我们的期望一致;然后,我们才能开始组装和测试整个系统。
为什么要使用单元测试
单元测试不但会使你的工作完成得更轻松,而且会令你的设计变得更好,甚至大大减少你花在调试上面的时间。
它的行为和我的期望一致吗?
最根本的,你想要回答下面这个问题:“这段代码达到我的目的了吗?”也许就需求而言,代码所做的是错误的事情,但那是另外一个问题了。你要的是代码向你证明它所做的就是你所期望的。
它的行为一直和我的期望一致吗?
许多开发者说他们只编写一个测试。也就是让所有代码从头到尾跑一次,只测试代码的一条正确执行路径,只要这样走一遍下来没有问题,测试也就算是完成了。 但是,现实生活当然不会这么事事顺心,事情也不总是那么美好:代码会抛出异常,硬盘会没有剩余空间,网络会掉线,缓冲区会溢出等——而我们写的代码也会出现bug。这就是软件开发的“工程”部分。就“工程”而言,土木工程师在设计一座桥梁的时候,必须考虑桥梁的负载、强风的影响、地震、洪水等等。电子工程师要考虑频率漂移、电压尖峰、噪音,甚至这些同时出现时所带来的问题。
你不能这样来测试一座桥梁:在风和日丽的某一天,仅让一辆车顺利地开过这座桥。显然,这种测试对于桥梁测试来说是远远不够的。类似地,在测试某段代码的行为是否和你的期望一致时,你需要确认:在任何情况下,这段代码是否都和你的期望一致;譬如在风很大、参数很可疑、硬盘没有剩余空间、网络掉线的时候。
我可以依赖单元测试吗?
不能依赖的代码是没有多大用处的。但更糟糕的是,那些你自认为可以信赖的代码(但是结果证明这些代码是有bug的)有时候也会让你花很多时间在跟踪和调试上面。显然,几乎没有项目可以允许你在这上面浪费太多的时间,因此无论如何,你都要避免这种“前进一步,后退两步”的开发方法。也就是说,要让开发过程保持稳定的步伐前进。
没人能够写出完美无缺的代码;但是这并没有关系——只要你知道问题的所在就足够了。
显然,我们希望能够依赖于所编写的代码,并且清楚地知道这些代码的功能和约束。 例如,假设你写了一个反转数值序列的方法。在测试的过程中,你也许会传一个空序列给这个程序——但导致了程序崩溃。实际上,程序并没有要求该程序必须能够接收一个空序列,因此你可以只在方法的注释中说明这个约束:如果传递一个空序列给这个方法,那么这个方法将会抛出一个异常。现在你马上就知道了该代码的约束,从而也就不需要用其他很麻烦的方法来解决这个问题(因为在某些地点要解决这个问题并不方便,比如在高空大气层中)。
单元测试说明我的意图了吗?
对于单元测试而言,一个最让人高兴的意外收获就是它能够帮助你充分理解代码的用法。从效果上而言,单元测试就像是能执行的文档,说明了在你用各种条件调用代码时,你所能期望这段代码完成的功能。
项目成员能够通过查看单元测试来找到如何使用你所写代码的例子。如果他偶然发现了一个你没有考虑到的测试用例,那么他也可以很快地知道这个事实:你的代码可能并不支持这个用例。 显然,在正确性方面,可执行的文档有它的优势。与普通的文档不同的是,单元测试不会出现与代码不一致的情况(当然,除非你选择不运行这些测试)。
如何进行单元测试
单元测试本来就是一项简单易学的技术;但是如果能够遵循一些指导性原则(guideline)和基本步骤,那么学习将会变得更加容易和有效。
首先要考虑的是在编写这些测试方法之前,如何测试那些可疑的方法。有了这样一个大概的想法之后,你将可以在编写实现代码的时候,或者之前,编写测试代码本身。
下一步,你需要运行测试本身,或者同时运行系统模块的所有其他测试,甚至运行整个系统的测试,前提是这些测试运行起来相对比较快。在此,我们要确保所有的测试都能够通过,而不只是新写的测试能够通过;这一点是非常重要的。也就是说,在保证不引入直接bug的同时,你也要保证不会给其他的测试带来破坏。
在这个测试过程中,我们需要确认每个测试究竟是通过了还是失败了——但这并不意味着你或者其他倒霉的人需要查看每个输出,然后才决定这些代码是正确的,还是错误的。在此,你慢慢地就会养成一个习惯:只要用眼睛瞄一下测试结果,就可以马上知道所有代码是否都是正确的,或者哪些代码是有问题的。关于这个问题,我们将留在讨论如何使用单元测试框架时来具体讨论。
不写测试的借口
在听过了我们合情合理、热情洋溢的阐述之后,某些开发者会点头并且同意单元测试的必要性,但是也许仍然会告诉我们由于某些原因,不能编写单元测试。下面就是我们所听到的一些借口。
编写单元测试太花时间了
对于开始做单元测试的初学者而言,这是出现次数最多的借口。当然,事实并不是这样的。首先,我们需要关注一下:在编写代码的时候,你在哪些地方花费了更多的时间。
大多数人都把测试看成某种只有在项目结束时才做的工作。但是,如果你到了那个时候才做单元测试的话,那么肯定会花费更多的时间。事实上,要想只在项目快要结束的时候才做单元测试,那简直是扯淡。
至少,我们可以这样来看待单元测试:假设你想用一台割草机来清除两英亩的草地。如果你在草很稀疏的时候就开始行动,那么工作将会很简单。但是如果你等到这块草地长上粗壮大树、缠绕灌木的时候才准备行动,那么工作将会变得非常困难
从图1.1中你可以看到,“立即测试”和“延后测试”之间并没有权衡可言;而是直线效率和指数效率之间的对比,而且对于后者而言,复杂度会不断增加,并且在项目后期,很多工作需要从头再来。所有这些额外的工作都会影响你的工作效率,如图1.1所示。
显然,单元测试也并非免费的午餐。在立即测试模型中,单元测试是有开销的(在时间和金钱上面)。但是如果你查看右边曲线的方向,你会发现它花费了更多的开销——效率曲线急剧下降;而且生产率甚至会变成负值;这些生产率损耗可以很容易导致一个项目失败。
因此,如果你仍然认为在编写产品代码的时候,还是没有时间编写测试代码,那么请先考虑下面这些问题:
1. 对于所编写的代码,你在调试上面花了多少时间?
2. 对于以前你自认为正确的代码,而实际上这些代码却存在重大的bug,你花了多少时间在重新确认这些代码上面?
3. 对于一个别人报告的bug,你花了多少时间才找出导致这个bug的源码位置?
对于那些没有使用单元测试的程序员而言,上面这些问题所耗费的时间的递增速度是很快的,而且随着项目的深入,递增速度会变得更快;而另一方面,适当的单元测试却可以很大程度地减少这些时间,从而能够为你腾出足够的时间来编写所有的单元测试——甚至可能还有剩余的空闲时间。
运行测试的时间太长了
合适的测试是不会让这种情况发生的。实际上,大多数测试的执行都是非常快的,因此你在几秒之内就可以运行成千上万个测试。但是有时某些测试会花费很长的时间,因此我们不能每次都运行这些测试,有时你就要暂时停止运行这些测试。
在这种情况下,需要把这些耗时的测试和其他测试分开。通常可以每天运行这种测试一次,或者几天一次;而对于运行很快的测试,则可以经常运行。
测试代码并不是我的工作
这是一个很有趣的借口。请问,到底什么是你的工作呢?假设你的工作只是为了编写产品代码。如果对于那些没有把握的代码,你就随便地扔给测试组,那么你实际上并没有完成你的工作。实际上,期望别人来清理我们的代码是很不好的做法;甚至在某种情况下,如果别人指出了一大摞有错误的代码,那么也意味着你的职位也就到此为止了。
另一方面,如果测试员或者QA(Quality Assurance)组发现很难在你的代码中挑出错误,那么你的名声将会一路高升——同时提升的还有你的职位。
我并不清楚代码的行为,所以也就无从测试 如果你实在不清楚代码的行为,那么估计现在并不是编码的时候。或许你应该先建立一个原型,这样才有助于你认清需求。如果你并不知道代码的行为,那么你又如何知道你编写的代码是正确的呢?
相关推荐
根据提供的文件信息,“单元测试之道Java版.pdf(高清)”主要介绍了在Java开发环境中进行单元测试的方法、策略及最佳实践等内容。以下将基于该文件的标题、描述、标签以及部分内容来详细阐述相关的知识点。 ### ...
《单元测试之道Java版-使用JUnit》是一本深入讲解如何在Java开发中运用JUnit进行单元测试的专业指南。单元测试是软件开发过程中的重要环节,它能够确保代码的正确性、可维护性和稳定性。JUnit作为Java领域最流行的...
在Java世界里,Junit是广泛使用的单元测试框架,它为开发者提供了方便、高效的测试工具。本篇文章将深入探讨如何利用Junit进行单元测试,并分享一些最佳实践。 首先,我们需要了解什么是单元测试。单元测试是对软件...
单元测试是软件开发过程中的重要环节,它旨在验证代码的各个独立模块是否按照预期...通过阅读《单元测试之道Java版使用JUnit》这本书,你可以深入了解如何有效地使用JUnit进行单元测试,以及如何构建强大的测试框架。
《单元测试之道Java版:使用JUnit》是一本深入讲解如何在Java开发中运用JUnit进行高效自动化测试的专业书籍。JUnit作为Java领域最广泛使用的单元测试框架,对于任何希望提升软件质量、确保代码健壮性的开发者来说,...
在Java领域,JUnit是最常用的单元测试框架,它的应用广泛且功能强大。本文将深入探讨如何利用JUnit进行有效的单元测试,以及它为开发者带来的益处。 JUnit是一个开源的Java库,专门用于编写和运行Java程序的单元...
"SpringTest_springtest_spring_java_Framework_"这个标题暗示了我们讨论的是关于Spring框架的测试方面,可能是使用Spring进行单元测试或集成测试的一些实践。 描述中的“简单小应用,实现了一些基本的功能”可能是...
总结起来,Java单元测试之道在于理解单元测试的重要性,熟练运用JUnit等测试框架,编写详尽的测试用例,以及将测试集成到开发流程中。通过这种方法,开发者可以构建出更加健壮、易于维护的软件系统。
Java 单元测试是软件开发过程中的重要环节,它允许开发者对代码的各个最小可测试单元进行验证,确保每个函数或方法在独立运行时能够按照预期工作。在Java世界里,JUnit是一个广泛使用的单元测试框架,它为编写和执行...
标题中的"Hello_java_test_"可能是指一个用于测试的Java项目或者是一个特定的Java类或方法名,这通常在开发过程中用来标识与测试相关的代码。描述中提到的"这个很好是不错的文件jar文件,我们都是这个要用的jar文件...
标题“ant_basic_test_ANT_JAVA_ACO_in1rr_”暗示了这是一个关于使用蚁群算法(ACO)在Java环境中实现的基本测试项目。蚁群优化算法是受到蚂蚁寻找食物路径行为启发的一种分布式优化算法,常用于解决组合优化问题,...
《单元测试之道Java版》是一本专为Java开发者设计的指南,主要聚焦于JUnit这个流行的单元测试框架。单元测试是软件开发过程中的重要环节,它能够确保代码的正确性、可维护性和可靠性。JUnit作为Java领域中最常用的...
标签“environmenttest.j java_测试”进一步确认了我们的理解,即“environmenttest.j”是一个Java源代码文件,而“测试”意味着它可能包含一系列的单元测试或者功能测试,用于验证J2ME环境的正确性和稳定性。...
描述中的“study_test_java”进一步确认了这个主题,但没有提供具体的信息,所以我们主要依赖标签“study_java_test”来推测其内容。 “study_java_test”标签可以理解为这是针对Java语言的学习与测试资源。这意味...
【描述】描述中的"Test_网上书店Java测试代码.zip"暗示了这个压缩包的核心内容是用于测试的Java源代码,这可能包括了单元测试、集成测试或系统测试,目的是确保网上书店系统的各个模块和功能正常运作。测试代码通常...
JUnit是Java编程语言中最常用的单元测试框架之一,它允许开发者对代码进行自动化测试,确保代码的质量和功能的正确性。本资源"JUnit_Test_Code.zip"包含了一份关于如何使用JUnit进行测试的实例,以及图文并茂的讲解...
单元测试是软件开发过程中的重要环节,特别是在Java编程中,它被视为确保代码质量、提高可维护性和降低缺陷率的关键步骤。单元测试是对程序中最小可测试单元进行检查和验证,通常是一个函数、方法或类。它的目的是...
### Geliang_单元测试模式与重构v1. #### 一、引言 单元测试作为软件开发过程中的一个重要组成部分,能够帮助开发者确保代码的质量,并在重构过程中保持系统的稳定性。本文档基于葛亮2013年7月4日的分享内容进行...
在Java开发过程中,单元测试是确保代码质量的重要环节。它帮助开发者发现并修复问题,提高软件的稳定性和可维护性。而代码覆盖率则是衡量单元测试有效性的一个关键指标,它表示了被测试代码被执行的程度。JaCoCo是一...