- 浏览: 1527254 次
- 性别:
- 来自: 厦门
博客专栏
-
Spring 3.x企业实...
浏览量:464195
文章分类
最新评论
-
JyeChou:
学习Spring必学的Java基础知识(1)----反射 -
hhzhaoheng:
...
《Spring4.x企业应用开发实战》光盘资料下载 -
renlongnian:
//assertReflectionEquals(user1, ...
单元测试系列之3:测试整合之王Unitils -
骑着蜗牛超F1:
huang_yong 写道我的经验是,只需定义三层:1.ent ...
Spring的事务管理难点剖析(2):应用分层的迷惑 -
wangyudong:
工具地址貌似更新了哦https://github.com/Wi ...
几种常用的REST webservice客户端测试工具
经过长时间积极的开发之后,JUnit 4.0 于今年年初发布了。JUnit 框架的某些最有趣的更改 —— 特别是对于本专栏的读者来说 —— 正是通过巧妙地使用注释实现的。除外观和风格方面的显著改进外,新框架的特性使测试用例的编制从结构规则中解放出来。使原来僵化的 fixture 模型更为灵活,有利于采取可配置程度更高的方法。因此,JUnit 框架不再强求把每一项测试工作定义为一个名称以 test
开始的方法,并且现在可以只运行一次 fixture,而不是每次测试都需要运行一次。
虽然这些改变令人欣慰,但 JUnit 4 并不是第一个提供基于注释的灵活模型的 Java™ 测试框架。在修改 JUnit 之前很久,TestNG 就已建立为一个基于注释的框架。
事实上,是 TestNG 在 Java 编程中率先 实现了利用注释进行测试,这使它成为 JUnit 的有力竞争对手。然而,自从 JUnit 4 发布后,很多开发者质疑:二者之间还有什么差别吗?在本月的专栏中,我将讨论 TestNG 不同于 JUnit 4 的一些特性,并提议采用一些方法,使得这两个框架能继续互相补充,而不是互相竞争。
您知道吗?
在 Ant 中运行 JUnit 4 测试比预计的要难得多。事实上,一些团队已发现,惟一的解决方法是升级到 Ant 1.7。
JUnit 4 和 TestNG 有一些共同的重要特性。这两个框架都让测试工作简单得令人吃惊(和愉快),给测试工作带来了便利。二者也都拥有活跃的社区,为主动开发提供支持,同时生成丰富的文档。
两个框架的不同在于核心设计。JUnit 一直 是一个单元测试框架,也就是说,其构建目的是促进单个对象的测试,它确实能够极其有效地完成此类任务。而 TestNG 则是用来解决更高 级别的测试问题,因此,它具有 JUnit 中所没有的一些特性。
初看起来,JUnit 4 和 TestNG 中实现的测试非常相似。为了更好地理解我的意思,请看一下清单 1 中的代码。这是一个 JUnit 4 测试,它有一个 macro-fixture(即仅在所有测试运行前调用一次的 fixture),这个 macro-fixture 由 @BeforeClass
属性表示:
package test.com.acme.dona.dep; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import org.junit.BeforeClass; import org.junit.Test; public class DependencyFinderTest { private static DependencyFinder finder; @BeforeClass public static void init() throws Exception { finder = new DependencyFinder(); } @Test public void verifyDependencies() throws Exception { String targetClss = "test.com.acme.dona.dep.DependencyFind"; Filter[] filtr = new Filter[] { new RegexPackageFilter("java|junit|org")}; Dependency[] deps = finder.findDependencies(targetClss, filtr); assertNotNull("deps was null", deps); assertEquals("should be 5 large", 5, deps.length); } } |
JUnit 用户会立即注意到:这个类中没有了以前版本的 JUnit 中所要求的一些语法成分。这个类没有 setUp()
方法,也不对TestCase
类进行扩展,甚至也没有哪个方法的名称以 test
开始。这个类还利用了 Java 5 的一些特性,例如静态导入,很明显地,它还使用了注释。
在清单 2 中,您可以看到同一个 测试项目。不过这次是用 TestNG 实现的。这里的代码跟清单 1 中的测试代码有个微妙的差别。发现了吗?
package test.com.acme.dona.dep; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import org.testng.annotations.BeforeClass; import org.testng.annotations.Configuration; import org.testng.annotations.Test; public class DependencyFinderTest { private DependencyFinder finder; @BeforeClass private void init(){ this.finder = new DependencyFinder(); } @Test public void verifyDependencies() throws Exception { String targetClss = "test.com.acme.dona.dep.DependencyFind"; Filter[] filtr = new Filter[] { new RegexPackageFilter("java|junit|org")}; Dependency[] deps = finder.findDependencies(targetClss, filtr); assertNotNull(deps, "deps was null" ); assertEquals(5, deps.length, "should be 5 large"); } } |
显然,这两个清单很相似。不过,如果仔细看,您会发现 TestNG 的编码规则比 JUnit 4 更灵活。清单 1 里,在 JUnit 中我必须把 @BeforeClass
修饰的方法声明为 static
,这又要求我把 fixture,即 finder
声明为 static
。我还必须把 init()
声明为public
。看看清单 2,您就会发现不同。这里不再需要那些规则了。我的 init()
方法既不是 static
,也不是 public
。
从最初起,TestNG 的灵活性就是其主要优势之一,但这并非它惟一的卖点。TestNG 还提供了 JUnit 4 所不具备的其他一些特性。
JUnit 框架想达到的一个目标就是测试隔离。它的缺点是:人们很难确定测试用例执行的顺序,而这对于任何类型的依赖性测试都非常重要。开发者们使用了多种技术来解决这个问题,例如,按字母顺序指定测试用例,或是更多地依靠 fixture 来适当地解决问题。
如果测试成功,这些解决方法都没什么问题。但是,如果测试不成功,就会产生一个很麻烦的后果:所有 后续的依赖测试也会失败。在某些情况下,这会使大型测试套件报告出许多不必要的错误。例如,假设有一个测试套件测试一个需要登录的 Web 应用程序。您可以创建一个有依赖关系的方法,通过登录到这个应用程序来创建整个测试套件,从而避免 JUnit 的隔离机制。这种解决方法不错,但是如果登录失败,即使登录该应用程序后的其他功能都正常工作,整个测试套件依然会全部失败!
与 JUnit 不同,TestNG 利用 Test
注释的 dependsOnMethods
属性来应对测试的依赖性问题。有了这个便利的特性,就可以轻松指定依赖方法。例如,前面所说的登录将在某个方法之前 运行。此外,如果依赖方法失败,它将被跳过,而不是标记为失败。
import net.sourceforge.jwebunit.WebTester; public class AccountHistoryTest { private WebTester tester; @BeforeClass protected void init() throws Exception { this.tester = new WebTester(); this.tester.getTestContext(). setBaseUrl("http://div.acme.com:8185/ceg/"); } @Test public void verifyLogIn() { this.tester.beginAt("/"); this.tester.setFormElement("username", "admin"); this.tester.setFormElement("password", "admin"); this.tester.submit(); this.tester.assertTextPresent("Logged in as admin"); } @Test (dependsOnMethods = {"verifyLogIn"}) public void verifyAccountInfo() { this.tester.clickLinkWithText("History", 0); this.tester.assertTextPresent("GTG Data Feed"); } } |
在清单 3 中定义了两个测试:一个验证登录,另一个验证账户信息。请注意,通过使用 Test
注释的 dependsOnMethods = {"verifyLogIn"}
子句,verifyAccountInfo
测试指定了它依赖 verifyLogIn()
方法。
通过 TestNG 的 Eclipse 插件(例如)运行该测试时,如果 verifyLogIn
测试失败,TestNG 将直接跳过 verifyAccountInfo
测试,请参见图 1:
对于大型测试套件,TestNG 这种不标记为失败,而只是跳过的处理方法可以减轻很多压力。您的团队可以集中精力查找为什么百分之五十的测试套件被跳过,而不是去找百分之五十的测试套件失败的原因!更有利的是,TestNG 采取了只重新运行失败测试的机制,这使它的依赖性测试设置更为完善。
在大型测试套件中,这种重新运行失败测试的能力显得尤为方便。这是 TestNG 独有的一个特性。在 JUnit 4 中,如果测试套件包括 1000 项测试,其中 3 项失败,很可能就会迫使您重新运行整个测试套件(修改错误以后)。不用说,这样的工作可能会耗费几个小时。
一旦 TestNG 中出现失败,它就会创建一个 XML 配置文件,对失败的测试加以说明。如果利用这个文件执行 TestNG 运行程序,TestNG 就只 运行失败的测试。所以,在前面的例子里,您只需重新运行那三个失败的测试,而不是整个测试套件。
实际上,您可以通过清单 2 中的 Web 测试的例子自己看到这点。verifyLogIn()
方法失败时,TestNG 自动创建一个 testng-failed.xml 文件。该文件将成为如清单 4 所示的替代性测试套件:
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"> <suite thread-count="5" verbose="1" name="Failed suite [HistoryTesting]" parallel="false" annotations="JDK5"> <test name="test.com.acme.ceg.AccountHistoryTest(failed)" junit="false"> <classes> <class name="test.com.acme.ceg.AccountHistoryTest"> <methods> <include name="verifyLogIn"/> </methods> </class> </classes> </test> </suite> |
运行小的测试套件时,这个特性似乎没什么大不了。但是如果您的测试套件规模较大,您很快就会体会到它的好处。
TestNG 中另一个有趣的特性是参数化测试。在 JUnit 中,如果您想改变某个受测方法的参数组,就只能给每个 不同的参数组编写一个测试用例。多数情况下,这不会带来太多麻烦。然而,我们有时会碰到一些情况,对其中的业务逻辑,需要运行的测试数目变化范围很大。
在这样的情况下,使用 JUnit 的测试人员往往会转而使用 FIT 这样的框架,因为这样就可以用表格数据驱动测试。但是 TestNG 提供了开箱即用的类似特性。通过在 TestNG 的 XML 配置文件中放入参数化数据,就可以对不同的数据集重用同一个测试用例,甚至有可能会得到不同的结果。这种技术完美地避免了只能 假定一切正常的测试,或是没有对边界进行有效验证的情况。
在清单 5 中,我用 Java 1.4 定义了一个 TestNG 测试,该测试可接收两个参数:classname
和 size
。这两个参数可以验证某个类的层次结构(也就是说,如果传入 java.util.Vector
,则 HierarchyBuilder
所构建的 Hierarchy
的值将为 2
)。
package test.com.acme.da; import com.acme.da.hierarchy.Hierarchy; import com.acme.da.hierarchy.HierarchyBuilder; public class HierarchyTest { /** * @testng.test * @testng.parameters value="class_name, size" */ public void assertValues(String classname, int size) throws Exception{ Hierarchy hier = HierarchyBuilder.buildHierarchy(classname); assert hier.getHierarchyClassNames().length == size: "didn't equal!"; } } |
清单 5 列出了一个泛型测试,它可以采用不同的数据反复重用。请花点时间思考一下这个问题。如果有 10 个不同的参数组合需要在 JUnit 中测试,您只能写 10 个测试用例。每个测试用例完成的任务基本是相同的,只是受测方法的参数有所改变。但是,如果使用参数化测试,就可以只定义一个 测试用例,然后,(举例来说)把所需的参数模式加到 TestNG 的测试套件文件中。清单 6 中展示了这中方法:
<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd"> <suite name="Deckt-10"> <test name="Deckt-10-test"> <parameter name="class_name" value="java.util.Vector"/> <parameter name="size" value="2"/> <classes> <class name="test.com.acme.da.HierarchyTest"/> </classes> </test> </suite> |
清单 6 中的 TestNG 测试套件文件只对该测试定义了一个参数组(class_name
为 java.util.Vector
,且 size
等于 2
),但却具有无限的可能。这样做的一个额外的好处是:将测试数据移动到 XML 文件的无代码工件就意味着非程序员也可以指定数据。
尽管从一个 XML 文件中抽取数据会很方便,但偶尔会有些测试需要有复杂类型,这些类型无法用 String
或原语值来表示。TestNG 可以通过它的 @DataProvider
注释处理这样的情况。@DataProvider
注释可以方便地把复杂参数类型映射到某个测试方法。例如,清单 7 中的 verifyHierarchy
测试中,我采用了重载的 buildHierarchy
方法,它可接收一个 Class
类型的数据, 它断言(asserting)Hierarchy
的 getHierarchyClassNames()
方法应该返回一个适当的字符串数组:
清单 7. TestNG 中的 DataProvider 用法
package test.com.acme.da.ng; import java.util.Vector; import static org.testng.Assert.assertEquals; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import com.acme.da.hierarchy.Hierarchy; import com.acme.da.hierarchy.HierarchyBuilder; public class HierarchyTest { @DataProvider(name = "class-hierarchies") public Object[][] dataValues(){ return new Object[][]{ {Vector.class, new String[] {"java.util.AbstractList", "java.util.AbstractCollection"}}, {String.class, new String[] {}} }; } @Test(dataProvider = "class-hierarchies") public void verifyHierarchy(Class clzz, String[] names) throws Exception{ Hierarchy hier = HierarchyBuilder.buildHierarchy(clzz); assertEquals(hier.getHierarchyClassNames(), names, "values were not equal"); } } |
dataValues()
方法通过一个多维数组提供与 verifyHierarchy
测试方法的参数值匹配的数据值。TestNG 遍历这些数据值,并根据数据值调用了两次 verifyHierarchy
。在第一次调用时,Class
参数被设置为 Vector.class
,而 String
数组参数容纳 “java.util.AbstractList
” 和 “ java.util.AbstractCollection
” 这两个 String
类型的数据。这样挺方便吧?
我已经探讨了对我而言,TestNG 的一些独有优势,但是它还有其他几个特性是 JUnit 所不具备的。例如 TestNG 中使用了测试分组,它可以根据诸如运行时间这样的特征来对测试分类。也可在 Java 1.4 中通过 javadoc 风格的注释来使用它,如 清单 5所示。
正如我在本文开头所说,JUnit 4 和 TestNG 在表面上是相似的。然而,设计 JUnit 的目的是为了分析代码单元,而 TestNG 的预期用途则针对高级测试。对于大型测试套件,我们不希望在某一项测试失败时就得重新运行数千项测试,TestNG 的灵活性在这里尤为有用。这两个框架都有自己的优势,您可以随意同时使用它们。
评论
发表评论
-
几种常用的REST webservice客户端测试工具
2015-07-30 16:11 9719引言 开发了Rest WebService服务后,如果方便快捷 ... -
dbunit经典的NoSuchColumnException解决之道
2013-08-20 14:36 7350抱怨 dbunit这么多人用 ... -
关于Idea testng单元测试乱码的解决
2013-07-02 10:50 14169问题 我是使用intel ... -
HyperSQL 2.0
2012-11-01 15:15 1962HyperSQL 2.0于本月7日发布了。此次发布距HSQLD ... -
Mockito
2012-06-07 16:40 7562Overview Packag ... -
探索 JUnit 4.4 新特性
2012-05-05 14:51 1794摘自:http://www.ibm.com 随着当前 ... -
单元测试系列之5:使用unitils测试Service层
2012-04-14 10:48 18445引述:Spring 的测试框架为我们提供一个强大的测试环境,解 ... -
单元测试系列之4:使用Unitils测试DAO层
2012-04-12 16:32 19683Spring 的测试框架为我们提供一个强大的测试环境,解 ... -
dbunit和poi版本不兼容的问题
2012-04-07 14:59 6961使用unitiles+dbunit 2.4.8,由于我的 ... -
单元测试系列之2:模拟利器Mockito
2012-03-30 11:38 15312引述:程序测试对 ... -
单元测试系列之1:开发测试的那些事儿
2012-03-28 12:52 10014引述:程序测试对保障应用程序正确性而言,其重要性怎 ...
相关推荐
【追求代码质量:Junit4与TestNG的对比】 在软件开发中,代码质量的保证是至关重要的,而单元测试是确保代码质量的一种有效手段。本文主要探讨了两个流行的Java测试框架——JUnit 4和TestNG,它们都是用于编写和...
赠送源代码:junit-jupiter-api-5.4.2-sources.jar; 赠送Maven依赖信息文件:junit-jupiter-api-5.4.2.pom; 包含翻译后的API文档:junit-jupiter-api-5.4.2-javadoc-API文档-中文(简体)-英语-对照版.zip; Maven...
赠送源代码:junit-jupiter-engine-5.8.2-sources.jar; 赠送Maven依赖信息文件:junit-jupiter-engine-5.8.2.pom; 包含翻译后的API文档:junit-jupiter-engine-5.8.2-javadoc-API文档-中文(简体)版.zip; Maven...
赠送源代码:junit-platform-launcher-1.8.0-M1-sources.jar; 赠送Maven依赖信息文件:junit-platform-launcher-1.8.0-M1.pom; 包含翻译后的API文档:junit-platform-launcher-1.8.0-M1-javadoc-API文档-中文(简体...
赠送源代码:junit-jupiter-api-5.8.2-sources.jar; 赠送Maven依赖信息文件:junit-jupiter-api-5.8.2.pom; 包含翻译后的API文档:junit-jupiter-api-5.8.2-javadoc-API文档-中文(简体)版.zip; Maven坐标:org....
- **灵活性**:TestNG比JUnit更加灵活,特别是在并行执行、数据驱动测试和依赖管理方面。 - **学习曲线**:JUnit的学习曲线相对较低,而TestNG则可能需要更多时间去掌握其高级特性。 - **社区和文档**:JUnit由于...
赠送源代码:junit-jupiter-engine-5.8.0-M1-sources.jar; 赠送Maven依赖信息文件:junit-jupiter-engine-5.8.0-M1.pom; 包含翻译后的API文档:junit-jupiter-engine-5.8.0-M1-javadoc-API文档-中文(简体)版.zip;...
赠送源代码:junit-jupiter-api-5.8.0-M1-sources.jar; 赠送Maven依赖信息文件:junit-jupiter-api-5.8.0-M1.pom; 包含翻译后的API文档:junit-jupiter-api-5.8.0-M1-javadoc-API文档-中文(简体)-英语-对照版.zip...
赠送源代码:junit-4.11-sources.jar; 赠送Maven依赖信息文件:junit-4.11.pom; 包含翻译后的API文档:junit-4.11-javadoc-API文档-中文(简体)版.zip; Maven坐标:junit:junit:4.11; 标签:junit、jar包、java、...
JUnit是Java编程语言中最常用的单元测试框架之一,它极大地简化了对代码进行验证和调试的过程。Junit4.12是该框架的一个稳定版本,它...正确理解和使用Junit4.12,对于确保代码质量和提升开发效率有着至关重要的作用。
赠送源代码:junit-4.13.1-sources.jar; 赠送Maven依赖信息文件:junit-4.13.1.pom; 包含翻译后的API文档:junit-4.13.1-javadoc-API文档-中文(简体)版.zip; Maven坐标:junit:junit:4.13.1; 标签:junit、中文...
赠送源代码:junit-4.12-sources.jar; 包含翻译后的API文档:junit-4.12-javadoc-API文档-中文(简体)版.zip 对应Maven信息:groupId:junit,artifactId:junit,version:4.12 使用方法:解压翻译后的API文档,...
总的来说,“junit-libs”压缩包是为了简化Java项目中引入JUnit的步骤,让开发者能快速利用JUnit进行单元测试,从而提高代码质量和项目的稳定性。理解和熟练使用JUnit是每一个Java开发者必备的技能之一。
赠送源代码:junit-4.13.2-sources.jar; 赠送Maven依赖信息文件:junit-4.13.2.pom; 包含翻译后的API文档:junit-4.13.2-javadoc-API文档-中文(简体)版.zip; Maven坐标:junit:junit:4.13.2; 标签:junit、中文...
5. **依赖注入**:TestNG支持依赖注入,允许测试类直接使用实际应用中的对象,简化了测试代码。 6. **异常处理**:TestNG允许你在`@Test`注解中指定预期的异常类型,如果测试方法没有抛出预期的异常,测试将失败。 ...
junit-vintage-engine-5.6.2.jarjunit-vintage-engine-5.6.2.jarjunit-vintage-engine-5.6.2.jar
4. **可扩展性**:JUnit 4.11允许用户自定义规则(Rules),通过@Rule注解,可以创建复杂的测试行为,如临时文件管理、超时控制等。 5. **分类(Categories)**:新增了测试分类功能,开发者可以将测试分为不同的...