在软件开发的早期阶段,选择合适的测试工具至关重要。一些工具虽然易于上手,但很快就会因功能不足而被抛弃。另一些工具则需要复杂的设置和培训,使得开发者难以快速尝试并确定其是否适合项目需求。Cucumber JVM是一个增强JUnit的测试框架,它提供了一种更简单的方法来开始行为驱动开发(BDD)。Gherkin语言(Cucumber理解的语言)允许软件或质量工程师更容易地用文字描述软件应用的预期行为场景。
对来说,Cucumber允许表达自动化的场景,而无需“深入代码”去阅读Java代码。这篇简短的博客文章描述了为什么使用它,以及使用它的一些可能与大多数人不同的技巧。
相信软件需求的规范更像是一种艺术形式。如果可以问业务专家他们希望应用程序做什么,并让他们抽象地提供每个细节,那将是非常好的。不幸的是,在过去30多年的每个项目中,遇到的都是不可避免的情况:在开发过程中途,需求发生变化或被误解,这使得对尚未实现的所有需求产生怀疑。
作为敏捷方法论的支持者,相信最好的应用程序是随着时间的推移而演变的。需求必须随着时间的推移而成形。这个概念让严格的项目经理甚至利益相关者感到疯狂。“如果不知道每个版本中确切的需求是什么,怎么能计划三年呢?”
这个话题可以有自己的博客,但目前让简单地预期项目的所有阶段都会出现需求变化。能够快速有效地做出反应就是敏捷开发的核心。
Cucumber和JUnit是允许自动化应用程序的重要功能和使用场景的工具。作为一个敏捷开发者,必须预期会根据需求变化重构代码和设计;编写的单元和集成测试给了信心,过去测试的场景仍然有效。这就是进行单元测试的原因,但仍然存在为什么使用Cucumber的问题。
对来说,Cucumber出现在领导的一个项目决定转向JEE6、CDI和拦截器的时候。这些概念都重用了服务,并使用面向方面的编程将业务规则行为注入到服务方法中。也就是说,决定使用Java拦截器来确保在服务方法调用开始之前满足业务规则。
那么,如何测试这样的想法呢?不仅需要测试业务逻辑是否正确,还需要一个测试环境来模拟容器行为,以便代码进行适当的集成测试。
当时,实际上只有一套工具可以处理这个环境:Cucumber和Arquillian。Arquillian是Redhat的一个测试框架,它允许“压缩包装”一个可部署的资源,可以在测试容器中使用,以便集成测试实际上是在容器中运行的。如何设置和使其工作是一个比在这里要覆盖的更高级的话题,但是从Arquillian Cucumber测试环境开始并不是“试水”的情况;这更像是在寻找水母。
无论如何,这种测试需求驱使更多地了解Cucumber作为测试工具,这让看到了BDD环境的许多可能性。
现在可能是一个很好的示例。到目前为止,这听起来可能像是一个推销,但让展示一下Cucumber测试是什么样子的。让以1040EZ表为例。如果为这个应用程序编写应用程序,可能需要如下场景:
Feature: 表单1040EZ
Scenario: 过多的应税利息
Given 标准错误消息
And 表单第1行包含$30,000.00
And 表单第2行包含$1,501.00
When 表单提交
Then 显示错误消息id F1040EZ-1
让剖析这个场景。特性和场景的措辞只是用来帮助描述正在测试的一般内容以及特定场景的内容。
“Given”、“When”和“Then”的语法是常用的测试规范术语,用来区分场景的不同部分。
“Given”用于描述测试的设置。“When”通常描述正在测试的Java方法以及提供给它的参数。例子没有参数。“Then”用于描述执行方法的结果。有时有消息;其他时候是预期的特定数据;其他情况下,只是测试没有破坏或改变。
如果看这个简单的税表,可能会发现许多其他场景,比如:
在这个简单的税例中有许多案例。对于更复杂的表格,可能有数百个可能的场景需要测试。测试的艺术是找到适当、可管理的测试数量来编写。
对于简单场景,“Given”-“When”-“Then”文本将存储在Java代码的资源中,作为一个“.feature”文件。所有特性文件通常存储在一起,一组文件夹模仿Java包结构。事实上,为了使测试变得容易,创建的Java类用于测试这个特性,它位于一个与这个文件夹结构匹配的包中。
Java类,在Cucumber中被称为步骤文件,定义了每个步骤要运行的Java代码,这只是一个“Given”、“When”或“Then”。一个步骤是使用@Given、@When和@Then注解匹配特性步骤的,这些注解有正则表达式参数,用于匹配。
请参阅Cucumber文档以获得更好的示例和更多关于如何将所有内容组合在一起的详细信息。
如上例所示,“Given”步骤设置了场景。在简单场景中,第一个“Given”似乎隐藏了一些重要数据;具体来说,应用程序的标准消息是什么?更复杂的场景可能依赖于大量预定义的数据。
Cucumber提供了列出给定数据的方法,以便可以在每个“Given”步骤中非常明确,并显示依赖的设置数据。然而,这将使即使是最简单的场景也变得如此冗长,以至于没有人愿意阅读它们。
在当前的项目中,已经将集成测试推进到有数百行设置数据的地步。所以大多数场景都以:
Given 标准值用于……
“……”被替换为列出应该为哪些类的对象提供标准值的关键字。这使得每个场景简洁且不杂乱,但如果一个非开发人员查看场景,他们怎么知道标准值是什么?
解决方案是提供一个StandardObjects.feature文件。这个文件像所有其他文件一样以“Given 标准值用于……”开始,但每个场景都有一个Then步骤,显示该标准对象的预期值表。
在上面的例子中,有:
Given 标准错误消息
使用这个STANDARD值特性方法,将重用那个Given,并提供如下Then:
Then 标准错误消息将包括以下内容:
| id | message |
| F1040EZ-1 |如果应税利息超过$1500,不能使用1040EZ |
| F1040EZ-2 |第X行是必需的 |
| F1040EZ-3 |第X行的值无效 |
将标准值期望分开到一个单独的特性声明中,可以清理功能性场景,但仍然可以查看标准值是什么。然而,请注意,随着标准值的变化或增加,特性文件必须更新。但认为这是一件好事。