测试基于Web的Spring应用程序(第一部分)

一位LJC(伦敦Java社区)会员及TDD/ BDD倡导者最近开始使用日益流行的Spring stack开发应用程序,他问我如何在这种环境下怎样能够最好地实施测试驱动。于是我开始给他回复邮件。突然间我发现这些信息可能对其他人也会所有帮助,于是便有了这篇文章。希望能够对你有所帮助。

一些假设

接下来的内容基于以下假设:

  • 应用程序使用以下Spring stack组件用Java编程实现:CoreMVCDataSecurity等,但是不包括Spring集成或Batch(尽管针对它们的测试框架和工具可能非常类似,但是测试这些组件的方法会有显著不同,因此我会用另一篇博客专门讨论)
  •  “终端用户”通过基于WebUI或者基于HTTPAPI与开发的应用程序进行交互(通常但不完全是REST风格)
  • 在产品中会使用外部数据存储为应用程序内部状态进行持久化。假定你也希望对应用程序这部分进行测试。
  • 除了使用类似Jetty这样的简单内存servlet容器进行应用部署,我们不会讨论其他远程或嵌入容器。类似Arquillian这样的框架提供了很多非常酷的功能,支持对实际(嵌入式)容器进行单元测试,比如TomcatGlassFishJBoss应用服务器。通过模拟与产品类似的打包(“ShrinkWrapping“)机制可以使用你最喜爱的测试框架,比如JUnit,对任意一组功能子集(可以下到Class级别)在嵌入式容器内进行测试。

一般建议

如果你是测试驱动开发新手,推荐你阅读以下书籍:

  • Effective Unit Testing: A guide for Java developers :这本书很好地诠释了Java单元测试领域所有(最新)的关键概念和技术。该书不但涵盖了单元测试的动机和方法,而且还介绍了如何编写具有表现力和可维护性的“好”测试,并且针对每个使用场景如何选择最佳技术给出了建议。
  • Practical Unit Testing with JUnit and Mockito 和 Practical Unit Testing with TestNG and Mockito。这两本中包含了丰富的测试Java程序实际例子,并且讨论了为什么需要和如何进行测试驱动开发(TDD)。两本书非常相似,主要区别在于使用的测试框架。我的建议是购买TestNG和Effective Unit Testing(主要专注于JUnit)组合。这样可以了解两个最流行的基于Java的测试引擎差异和优势。
  • Growing Object-Oriented Software, Guided by Tests (Beck Signature) :这是一本经典TDD书籍,讨论了所有测试驱动应用设计的高层概念和动机。该书还提供了一个很好的示例,通过介绍在线拍卖网站的构建及演化展示了书中介绍的开发方法。
  • 还有几本介绍Javascript单元测试的书,这里就不做详细介绍了。但是推荐阅读可测试的JavaScript

Core测试

单元测试

目的: 应用程序的核心就是对问题建模,通常包含了与需要编写的“业务”规则相关的术语(例如,限制使用第三方代码)。

因此,提出代码通过完备地测试这种要求也是很容易的。不只因为实现的功能特殊,而且希望确保在集成到标准框架(比如持久化框架)时会得到预期的结果。在微观层面,好的测试也是代码的一种“注释”。

工具:

  • JUnit / TestNG – 运行Java测试的标准框架。我喜欢把Java测试做为Maven构建流程中的一部分通过surefire插件运行。
  • Spock – 一个很棒的基于Groovy的测试和需求框架。如果你从未听说过它,请暂停阅读本文访问它的主页。我是认真的。可以马上浏览一下Spock基础。有没有觉得它很棒?而且Spock还可和Spring一起使用
  • Mockito – 我最喜欢的mock框架。虽然有很多其它框架,但是我发现Mockito在易用性、表现力和可维护性之间找到了最佳平衡点。
  • PowerMock – 一个很棒的Mockito 扩展,可以应对诸如依赖遗留函数库静态方法,mock privatefinal函数,mock对象初始化这样的难题。我的建议是如果你确定需要使用PowerMock 时,请再次检查你的功能设计——通常需要使用PowerMock 意味着测试“有异味 ”,可能意味着需要改善你的设计,或者代码接口的外部依赖需要改进。例如,与其对依赖遗留函数库静态构造方法进行mock,为什么不(使用装饰器/适配器模式)在自己定义的接口里隐藏这个方法?这样会使得mock更简单。
  • ConcurrentUnit - 一个优秀的工具类框架,适用于测试组件间困难的任务。
  • Make-it-easy – 一个创建测试数据的精巧框架(其中的概念来自前文提到的Growing Object-Oriented Software, Guided by Tests
  • · junitparams – 一个扩展了JUnits参数化测试的优秀框架(也许正如它的作者声称的那样——“这是改头换面的参数化测试”)

行动:

  • 分离测试Class和组件。单元测试应当专注于小的工作单元,因此依赖关系(或者mock的依赖关系)应减到最小
  • 创建覆盖代码所有路径的测试用例,不仅仅是喜欢的路径
  • 创建复制边界测试用例
  • 利用参数化测试快速添加新发现数据驱动测试用例

不要做:

  • 编写平常的测试提高单元测试覆盖率
  • 创建容易失败的测试以检测代码最细微的修改。例如,如果你用了10行以上的代码构建mock和结果,这是有问题的
  • 测试getter和setter函数,除非它们包含业务逻辑
  • 测试任何与底层操作系统(文件、网络等)、数据存储或者容器/服务器直接交互的功能。这种交互应当在测试层进行mock

连接到一起

集成测试——持久化层

目的: 考虑一下测试持久化或DAO层,这是你想要模拟外部资源的行为而不是自身功能。

工具:

  • 所有上文提到的单元测试工具,再加上…
  • 利用Spring的@ContextConfiguration 管理所有依赖关系。例如EntityManager或MongoOperations以及AbstractTransactionalJUnit4SpringContextTests 。通过定义明确的事务语义管理测试运行。
  • 已选择的数据存储嵌入式(内存)版。例如
    • H2 – 一个优秀的嵌入式SQL数据存储,99% 的测试用例执行结果与MySQL 相同(实际结果因人而异)
    • 嵌入式MongoDB - 满足所有MongoDB数据存储要求(对2.9版本之前的MongoDB,请移步另一个我参与的项目SDFongo
    • 嵌入式Solr 通过ZoomInfo的InProcessSolrServer发布,由我负责更新(无耻的自我宣传)
    • ActiveMQ – 作为嵌入式和非持久化运行

行动:

  • 对有所持久化对象进行assert。我已经记不清有多少次因为配置XToMany JPA错误导致级联的子实例错误了
  • 以合理的缩短测试执行时间作为目标
  • 测试所有DAO/版本库接口的public方法
  • 创建支持载入固有测试数据的测试用具。可以考虑使用SpringAbstractTransactionalJUnit4SpringContextTests提供的executeSqlScript() 
  • 在每个测试结束销毁(或者删除)数据,确保之前测试的数据不会影响结果。这对那些布恩那个确保测试执行顺序的测试框架尤其重要!

不要做:

  • 过度重复的单元测试。例如,大多数业务逻辑会调用持久化层载入数据,处理以后再调用持久化层保存结果。在这一层的测试只需要专注于载入和保存
  • 测试每一个可以想到的数据排列
  • 将许多操作串联在一起——通常这是服务集成测试或点对点测试

2部分即将完成

我很快发布第2部分内容,这一部分将会讨论以下主题,包括服务层集成测试、基于Web的测试和API测试。第3部分将会讨论点对点(E2E)测试和行为驱动开发(BDD)。

原文链接: javacodegeeks 翻译: ImportNew.com - 唐尤华
译文链接: http://www.importnew.com/5268.html
[ 转载请保留原文出处、译者和译文链接。]

关于作者: 唐尤华

我喜欢程序员,他们单纯、固执、容易体会到成就感;面对压力,能够挑灯夜战不眠不休;面对困难,能够迎难而上挑战自我。他们也会感到困惑与傍徨,但每个程序员的心中都有一个比尔盖茨或是乔布斯的梦想“用智慧开创属于自己的事业”。我想说的是,其实我是一个程序员。(新浪微博:@唐尤华

查看唐尤华的更多文章 >>



相关文章

发表评论

Comment form

(*) 表示必填项

还没有评论。

跳到底部
返回顶部