2013年测试基于Web的Spring应用程序(第1部分)
LJCer和TDD / BDD的一位拥护者最近开始使用广受欢迎的Spring堆栈开发应用程序,他就如何最好地在这种情况下实施测试驱动的方法向我提出了建议。 我开始回覆一封电子邮件作为回应,但后来我突然想到这也可能对其他人有用……所以在这里–我希望它能对您有所帮助
假设条件
本文的其余部分假定以下内容:
- 应用程序将使用Spring堆栈的以下组件在Java中实现:核心,MVC,数据,安全性等,但我们将不涉及Spring Integration或Batch(尽管推荐的测试框架和工具可能非常相似,但我发现测试这些组件的技术可能大不相同,因此必须等到另一篇博客文章为止)
- “最终用户”将通过基于Web的UI和/或基于HTTP的API与开发的应用程序进行交互(通常,但不仅限于REST)
- 为了进行生产,将使用外部数据存储在应用程序中保持长期状态。 我们假设您也将要测试您的应用程序如何与此交互。
- 除了使用简单的内存中的servlet容器(如Jetty)来部署整个打包的应用程序之外,我们将不涉及通过远程或嵌入式容器进行的测试。 诸如Arquillian之类的框架提供了一些非常酷的功能,这些功能允许针对实际(嵌入式)容器(例如Tomcat,GlassFish和JBoss Application Server)进行单元测试。 这是通过提供机制来模拟生产类包装(“ ShrinkWrapping ”)和在嵌入式容器内测试组件的任意子集(直至类级别)的方式来实现的,所有这些过程都可以通过您喜欢的测试框架进行,例如JUnit的。
一般建议
如果您是新的测试驱动开发人员,那么我推荐以下书籍:
- 有效的单元测试:Java开发人员指南 -这是对基于Java的单元测试中所有关键概念和技术的出色介绍(并且是最新的)。 本书不仅涵盖了动机和方法,而且还介绍了如何编写具有表现力和可维护性的“良好”测试,并针对每种用例选择了最合适的技术。
- 使用JUnit和Mockito进行 实用单元测试和/或使用TestNG和Mockito进行实用单元测试 。 这些都是很棒的书,其中包含有关如何测试Java应用程序的大量实用示例,还讨论了TDD的原因和方式。 这两本书非常相似,主要区别在于用于驱动测试的框架。 我的建议是购买结合“有效单元测试”(主要侧重于JUnit)的TestNG版本,因为很高兴知道两种最流行的基于Java的测试引擎的区别和优势/劣势。
- 由测试指导的不断增长的面向对象软件(贝克签名) –这是经典的TDD书籍之一,它讨论了测试驱动应用程序设计的所有高级概念和动机。 本书还详细介绍了基于拍卖网站的整个应用程序的构建和演进过程,为如何使用规定的方法提供了一个很好的实践示例。
- 有几本关于单元测试JavaScript的书,尽管我没有机会阅读所有这些书,但我还是推荐Testable JavaScript。
测试核心
单元测试
目的 :应用程序的核心对要解决的问题进行建模,通常包含特定于域的表示形式和相关的“业务”规则,这些规则主要由您负责编写(即,在此处可以重用第三方代码的机会是有点受限制)。
因此,很容易有人争辩说,该代码应该经过良好的测试-您在这里实现的组件不仅可能是唯一的,而且还希望在将它们连接到提供样板功能的框架(例如,坚持不懈。 编写良好的测试还可以在微观级别上“记录”您的代码(及相关功能)。
工具 :
- JUnit / TestNG –用于运行Java测试的事实上的框架。 我喜欢通过surefire插件将这些作为我的Maven构建过程的一部分运行
- Spock –基于Groovy的出色测试和规范框架。 如果您还没有听说过,请立即停止阅读并访问该网页。 说真的 现在查看Spock基础页面 。 那有多棒? 而且它在Spring中也能很好地发挥作用 !
- Mockito –我最喜欢的模拟框架。 其他确实存在,但我发现Mockito在易用性,表达性和可维护性之间取得了很好的平衡
- PowerMock –当您遇到Mockito无法解决的困难情况时(例如,模拟依赖静态方法的旧库,模拟私有或最终方法,或模拟对象实例化),Mockito的强大扩展。 我的建议是,在开始接触PowerMock时,始终对您的组件设计进行三重检查–通常,使用PowerMock的愿望表明测试“有异味”,并可能表明您应该改进组件设计或您的方式。具有外部依赖性的代码接口可以得到增强。 例如,除了模拟第三方库的静态构造方法之外,您是否可以不将此方法调用隐藏在自己的接口后面(遵循decoractor / adapter模式),这将简化模拟?
- ConcurrentUnit –一个强大的实用程序框架,用于测试组件内并发的艰巨任务
- 轻松制作 -一个用于创建“测试数据”构建器的小框架(其概念来自前述的“ 不断增长的面向对象的软件,由测试指导”)
- junitparams –一个很好的框架,用于增强JUnits参数化测试(或如创建者所言–“不烂的参数化测试”)
做 :
- 隔离测试中的类和组件。 单元测试应专注于测试小型工作单元 ,因此应具有最小的依赖关系(或应模拟依赖关系)
- 创建覆盖整个代码所有路径的测试用例,而不仅仅是简单的路径
- 创建测试复制边缘案例
- 利用参数化测试的功能来促进快速发现数据驱动的测试用例
不要 :
- 编写琐碎的测试只是为了增加单元测试的覆盖范围
- 创建脆弱的测试,使您的代码发生丝毫变化,即如果您使用超过10行代码来设置模拟和期望,那么您可能做错了
- 测试getter和setter,除非它们包含逻辑
- 直接测试与基础操作系统(文件,网络等),数据存储或任何容器/服务器的任何交互。 在此测试级别上应该模拟这种交互
塞在一起
集成测试–持久层
目的 :考虑对您的持久性或DAO层进行单元测试,但在这里您要模拟外部资源提供的行为,而不是模拟此功能。
工具 :
- 上面提到的所有单元测试工具以及…
- 利用Spring的@ContextConfiguration来管理诸如EntityManager或MongoOperations之类的依赖关系的连接,以及利用AbstractTransactionalJUnit4SpringContextTests来管理具有明确定义的事务语义的测试的运行
- 您选择的数据存储的嵌入式(内存中)版本,例如
- H2 –出色的嵌入式SQL数据存储区,在99%的标准用例中,其行为类似于MySQL(尽管您的里程可能有所不同……)
做 :
- 断言实体可以按您期望的那样持久。 我忘记了错误配置XToMany JPA批注的次数,这导致对子实体的级联操作不正确。
- 旨在使测试执行时间保持在合理范围内。
- 测试DAO /存储库界面上公开的所有公共方法
- 创建允许加载预先罐装的测试数据的线束。 从Spring的AbstractTransactionalJUnit4SpringContextTests中考虑executeSqlScript()
- 在每次测试结束时销毁(或删除)数据,以防止以前的测试数据影响结果。 如果您选择的测试框架不能保证执行测试的顺序,这一点尤其重要!
别
- 单元测试的覆盖范围过于重复,即大多数业务逻辑将调用持久层来加载数据,进行处理,然后再次调用持久层以保存结果。 此级别的测试只需要关注负载并保存
- 测试所有可能的数据排列
- 将太多的操作链接在一起–应该由服务集成测试或端到端测试覆盖
即将第二部分...
我将尽快尝试发布第2部分,它将涵盖诸如服务级别集成测试,基于Web的测试和API测试之类的主题。 第3部分将介绍端到端(E2E)测试和BDD。
翻译自: https://www.javacodegeeks.com/2013/07/testing-web-based-spring-applications-in-2013-part-one.html