• 软件测试技术
  • 软件测试博客
  • 软件测试视频
  • 开源软件测试技术
  • 软件测试论坛
  • 软件测试沙龙
  • 软件测试资料下载
  • 软件测试杂志
  • 软件测试人才招聘
    暂时没有公告

字号: | 推荐给好友 上一篇 | 下一篇

追求代码质量: 探究 XMLUnit

发布: 2008-4-03 17:31 | 作者: Andrew Glover | 来源: IBM | 查看: 58次 | 进入软件测试论坛讨论

领测软件测试网
Java™ 开发人员一般都很善于解决问题,所以由 Java 开发人员提出更容易的方法用以验证 XML 文档是很自然的事。本月,Andrew 将向您介绍 XMLUnit,一个能满足您所有的 XML 验证需求的 JUnit 扩展框架。

在软件开发周期中,需要不时地验证 XML 文档的结构或内容。不管构建的是何种应用程序,测试 XML 文档都具有一定的挑战性,尤其是在没有相关工具的情况下就更是如此。

本月,我将首先向您说明为何不能 使用 String 比较来验证 XML 文档的结构和内容。之后,我会介绍 XMLUnit,一个由 Java 开发人员创建并可服务于 Java 开发人员的 XML 验证工具,向您展示如何使用它来验证 XML 文档。

古典的 String 比较

首先,假设您已经构建了一个应用程序,该应用程序可以输出代表对象依赖性报告的 XML 文档。对于给定的类和对应的过滤器的集合,会生成一个报告来输出类和类的依赖项(想象一下导入)。

清单 1 显示了用于给定类列表(com.acme.web.Widgetcom.acme.web.Account)的报告,过滤器被设为忽略外部类,比如 java.lang.String


清单 1. 一个示例依赖性 XML 报告
				<DependencyReport date="Sun Dec 03 22:30:21 EST 2006">  <FiltersApplied>    <Filter pattern="java|org"/>    <Filter pattern="net."/>  </FiltersApplied>  <Class name="com.acme.web.Widget">    <Dependency name="com.acme.resource.Configuration"/>    <Dependency name="com.acme.xml.Document"/>  </Class>  <Class name="com.acme.web.Account">    <Dependency name="com.acme.resource.Configuration"/>    <Dependency name="com.acme.xml.Document"/>  </Class></DependencyReport>

清单 1 很明显是由应用程序生成的;因而,第一层测试就是验证应用程序是否真能生成一个文档。一旦验证了这一点,就可以继续测试指定文档的其他三个方面:

  • 结构
  • 内容
  • 指定内容

可以通过单独使用 JUnit 利用 String 比较处理上述前两个方面,如清单 2 所示:


清单2. 硬性验证 XML
				public class XMLReportTest extends TestCase { private Filter[] getFilters(){  Filter[] fltrs = new Filter[2];  fltrs[0] = new RegexPackageFilter("java|org");  fltrs[1] = new SimplePackageFilter("net.");  return fltrs; } private Dependency[] getDependencies(){  Dependency[] deps = new Dependency[2];  deps[0] = new Dependency("com.acme.resource.Configuration");  deps[1] = new Dependency("com.acme.xml.Document");  return deps; } public void testToXML() {  Date now = new Date();  BatchDependencyXMLReport report =    new BatchDependencyXMLReport(now, this.getFilters());  report.addTargetAndDependencies(    "com.acme.web.Widget", this.getDependencies());  report.addTargetAndDependencies(    "com.acme.web.Account", this.getDependencies());  String valid = "<DependencyReport date=\"" + now.toString() + "\">"+    "<FiltersApplied><Filter pattern=\"java|org\" /><Filter pattern=\"net.\" />"+    "</FiltersApplied><Class name=\"com.acme.web.Widget\">" +    " <Dependency name=\"com.acme.resource.Configuration\" />"+    "<Dependency name=\"com.acme.xml.Document\" /></Class>"+    "<Class name=\"com.acme.web.Account\">"+    "<Dependency name=\"com.acme.resource.Configuration\" />"+    "<Dependency name=\"com.acme.xml.Document\" />"+    "</Class></DependencyReport>";   assertEquals("report didn't match xml", valid, report.toXML()); }}

清单 2 中的测试有其他一些重大的缺陷 —— 而不仅仅是硬编码 String 比较那么简单。首先,测试并不真正可读。第二,它惊人的脆弱;一旦 XML 文档的格式改变(包括添加空格),与其尝试修复 String 本身,还不如粘贴进一个新的文档副本。最后,测试的本性会迫使您必须应付 Date 方面,虽然您并不想如此。

若想确保文档中第二个 Class 元素的 name 值是 com.acme.web.Account 又该如何呢?当然,您可以使用常规表达式或 String 搜索,但所需的工作量太大。这样看来,通过一个解析框架来操纵此 DOM 不是更有意义么?

XMLUnit 能否用于 TestNG?

XMLUnit 是一个 JUnit 扩展,但这并不意味着不能在 TestNG 使用它。只要它具有 API 而且此 API 支持委托同时不基于修饰器,那么您可以将几乎任何框架整合进 TestNG。

用 XMLUnit 进行测试

当您感觉自己为完成一项任务而努力过了头,您就可以想想解决此问题是否还有更容易的捷径可寻。如果所要解决的问题涉及的是编程式地验证 XML 文档,那么所应想到的解决方案就是 XMLUnit。

XMLUnit 是一种 JUnit 扩展框架,有助于开发人员测试 XML 文档。实际上,XMLUnit 是一种真正的 XML 测试的“多面手”:可以使用它来验证 XML 文档的结构、内容甚至该文档的指定部分。

最简单的做法是使用 XMLUnit 在逻辑上对比运行时 XML 文档和预定义的有效控制文件。本质上讲,这就是一种差异测试:假定一个 XML 文档是正确的,那么此应用程序在运行时是否会生成同样的东西?它是相对简单的一种测试,但也可以使用它来验证 XML 文档的结构和内容。也可以通过 XPath 的一点帮助来验证特定内容。

委托而非继承

首要原则是尽量避免测试用例继承。许多 JUnit 扩展框架,包括 XMLUnit,都提供可以通过继承得到的专门的测试用例来协助测试某一特定的架构。从框架继承来的测试用例都缺乏灵活性,这是 Java 平台的单一继承的范型所致。更多的时候,这些相同的 JUnit 扩展框架提供一个委托 API,此 API 可以更易于组合不同的框架,而无需采用严格的继承结构。

验证内容

可以通过委托或继承的方式使用 XMLUnit。作为最佳策略,我建议避免测试用例继承。另一方面,从 XMLUnit 的 XMLTestCase 继承确实可以提供一些方便的声明方法(这些方法不是静态 的,因而也就不能像 JUnit的 TestCase 声明一样被静态引用)。

不管您如何选择使用 XMLUnit,都必须实例化 XMLUnit 的解析器。您可以通过 System.setProperty 调用实例化它们,也可以通过 XMLUnit 核心类上的一些方便的 static 方法对它们进行实例化。

一旦用所需要的不同的解析器实例化 XMLUnit 之后,就可以使用 Diff 类,这是从逻辑上对比两个 XML 文档所需的中心机制。在清单 3 中,我利用 XMLUnit 对 >testToXML test 做了一些改进:


清单 3. 改进后的 testToXML 测试
				public class XMLReportTest extends TestCase { protected void setUp() throws Exception {		   XMLUnit.setControlParser(    "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");  XMLUnit.setTestParser(    "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");  XMLUnit.setSAXParserFactory(    "org.apache.xerces.jaxp.SAXParserFactoryImpl");  XMLUnit.setIgnoreWhitespace(true);    } private Filter[] getFilters(){  Filter[] fltrs = new Filter[2];  fltrs[0] = new RegexPackageFilter("java|org");  fltrs[1] = new SimplePackageFilter("net.");  return fltrs; } private Dependency[] getDependencies(){  Dependency[] deps = new Dependency[2];  deps[0] = new Dependency("com.acme.resource.Configuration");  deps[1] = new Dependency("com.acme.xml.Document");  return deps; } public void testToXML() {  BatchDependencyXMLReport report =     new BatchDependencyXMLReport(new Date(1165203021718L), 	  this.getFilters());  report.addTargetAndDependencies(    "com.acme.web.Widget", this.getDependencies());  report.addTargetAndDependencies(    "com.acme.web.Account", this.getDependencies());  Diff diff = new Diff(new FileReader(    new File("./test/conf/report-control.xml")),    new StringReader(report.toXML()));  assertTrue("XML was not identical", diff.identical());		 }}

注意一下我是如何实例化 XMLUnit 的 setControlParsersetTestParsersetSAXParserFactory 方法的。您可以为这些值使用任何兼容 JAXP 的解析器。还要注意我是用 true 调用 setIgnoreWhitespace 的 —— 这是一根救命稻草,相信我!否则,不一致的空白会导致很多故障。

延伸阅读

文章来源于领测软件测试网 https://www.ltesting.net/

TAG: XMLUnit

21/212>

关于领测软件测试网 | 领测软件测试网合作伙伴 | 广告服务 | 投稿指南 | 联系我们 | 网站地图 | 友情链接
版权所有(C) 2003-2010 TestAge(领测软件测试网)|领测国际科技(北京)有限公司|软件测试工程师培训网 All Rights Reserved
北京市海淀区中关村南大街9号北京理工科技大厦1402室 京ICP备2023014753号-2
技术支持和业务联系:info@testage.com.cn 电话:010-51297073

软件测试 | 领测国际ISTQBISTQB官网TMMiTMMi认证国际软件测试工程师认证领测软件测试网