Spock是针对Java和Groovy应用程序的测试和规范框架。 Spock是:
- 极富表现力
- 促进测试的给定/何时/然后语法
- 与大多数IDE和CI服务器兼容。
听起来不错? 通过快速访问Spock Web控制台,您可以非常快速地开始使用Spock。 当您有一个喜欢的小测试时,可以像发布此Hello World测试一样进行发布。
首先,Spock测试是用Groovy编写的。 这意味着,您在Java中拥有的一些样板代码将消失。 有一个Hello World测试可以很好地介绍Spock的某些功能。
- 默认情况下, 无需指示该类为公共类。
- 无需将firstWord和lastWord声明为字符串
- 无需显式调用assert,因为Expect块中的每一行代码都会自动获取该断言。 只要确保该块中的行是布尔表达式即可。 因此,在这种情况下,它只是一个相等表达式,可以为true或false。
如此少的样板代码下一步是什么? 好吧,您知道通过JUnit测试获得的那些真正长的测试名称,而不是必须调用此测试helloWorldIntroductionToSpockTest ()(这很难理解),您可以使用带空格的String来命名测试: Hello World对Spock的介绍测试 。 这使事情更具可读性。
第三,如果我要对测试进行一些小的更改并将firstWord更改为“ Hello1 ”,则测试当然会失败。 但是当我在Spock中失败时,我得到了测试的表达式的完整上下文。 我看到了firstWord的值, secondWord的值以及连接后的值,这使测试失败时可以更快地诊断问题。
模拟和存根对于介绍来说不错。 现在让我们看看更多功能。
在JUnit中( 以及各种附加功能),模拟和存根功能更为强大。 但是,它不仅在Spock中超级强大,而且非常简洁,使您的测试代码非常整洁且易于阅读。
假设我们想在测试中添加一个名为PaymentCalculator的类,更具体地说,是一个方法calculate(Product product,Integer count)。 在存根版本中,无论产品值如何,我们都希望返回计数乘以10。 在Spock中,我们通过以下方式实现这一目标:
PaymentCalculator paymentCalculator = Stub(PaymentCalculator)
paymentCalculator.calculate(_, _) >> {p, c -> c * 10}
如果您还没有意识到这是多么短暂和整洁,那么那就给自己喝杯咖啡吧 。 如果您已经很好地了解了,您仍然可以找到一个保险箱,但是请考虑以下几点:
- 计算值中的下划线表示所有值
- 在右侧,我们看到了Groovy Closure。 现在,将其视为具有两个输入的匿名方法。 p为产品,c为计数。 我们不必键入它们。 那只是更多的样板代码而已。
- 闭包将始终返回计数时间10。我们不需要return语句。 总是返回最后一个表达式的值。 同样,这意味着更少的样板代码。 当存根变得如此简单和整洁时,这意味着您可以真正专注于测试-很棒。
参数化测试
最好的解释方式是通过示例。
@Unroll
def "Check that the rugby player #player who has Irish status #isIrish plays for Ireland"(String player, Boolean isIrish) {given:"An instance of Rugby player validator"RugbyPlayerValidator rugbyPlayerValidator = new RugbyPlayerValidator()expect:rugbyPlayerValidator.isIrish(player) == isIrishwhere:player || isIrish"Johny Sexton" || true"Stuart Hogg" || false"Conor Murray" || true"George North" || false"Jack Nowell" || true}
在此参数化测试中,我们看到以下内容:
- 测试已参数化,我们在测试签名和where块中。
- 有一个输入参数播放器和一个输出参数–对应于期望值。
- 测试被参数化五次。 输入参数在左侧,输出在右侧。 当然,也可以有更多的一个,在这个测试中,我们每个只有一个。
- @Unroll注释表示如果测试失败,则将输出所有参数的值。 该消息将把玩家的详细信息替换为#player,并将爱尔兰身份的详细信息替换为#isIrish。 因此,例如,“ 检查具有爱尔兰身份的橄榄球运动员杰克·诺维尔是否为爱尔兰效力 ”
同样,这使得缩小错误的范围变得更快。 测试是否错误或代码是否错误? 这成为一个可以更快回答的问题。 在这种情况下,测试是错误的。
Groovy的所有好处
还有什么? 好吧,另一个主要好处是Groovy的所有好处。 例如,假设您正在测试一个返回JSON或XML的API。 Groovy在解析XML和JSON方面非常出色。 假设我们有一个API,该API以XML格式返回有关体育运动员的信息。 格式有所不同,但仅略有不同,具体取决于他们参加的运动:
Joey Carberry<details><rugbysummarycategory><players><player>Joey Carberry</player><player>Teddy Thomas</player></players>
</rugbysummarycategory>
</details><details><footballsummarycategory><players><player>Lionel Messi</player><player>Cristiano Ronaldo</player></players></footballsummarycategory>
</details>
我们只想调用此API,然后解析运动员(与运动无关)即可。 我们可以在Groovy中非常简单地将其多态解析。
def rootNode = new XmlSlurper().parseText(xml)
List players = rootNode.'*'.Players.Player*.text()
一些要点:
- 动态类型的功能是立竿见影的。 可以在rootNode上动态调用该表达式。 无需冗长,复杂的XPath表达式。
- “ *”就像通配符。 这将涵盖RugbySummaryCategory和FootballSummaryCategory。
- Player *,表示所有Player元素。 所以这里不需要愚蠢的冗长的循环
- text()表达式只是提取各个Player元素之间的文本值。 那么为什么现在要列出所有玩家并可以简单地进行操作:players.size()== 4请记住,不需要断言。
假设我们要检查玩家的名字。 好吧,在这种情况下,我们不在乎顺序,因此将列表转换为Set然后进行检查更有意义。 简单。
players as Set = ["Joey Carberry", "Teddy Thomas", "Lionel Messi", Cristiano Ranaldo"] as Set
这会将两个列表都转换为Set,这意味着订单检查消失了,这只是Set的比较。 我们可以利用更多的Groovy优势。 但是美丽是,我们实际上没有必要。
所有Java代码在Groovy类中也有效 。 Spock也是如此。 这意味着对于任何有Java背景的人来说,学习者的学习曲线都不陡峭。 他们可以编写纯Java代码,然后从代码评论等中获得一些Groovy技巧。
强大的注释
Spock还为您的测试提供了一系列功能强大的注释。 再次,我们在这里看到了Groovy的强大功能,因为我们可以将闭包传递给这些注释。 例如:
@IgnoreIf({System.getProperty("os.name").contains("windows")})
def "I'll run anywhere except windows"() {...}
或者如果执行时间过长,则使测试失败
@Timeout(value = 100, unit=TimeUnit.MILLISECONDS)
def "I better be quick"() {...}
因此,总之,Spock与普通JUnit具有以下优点:
- 强制执行测试结构。 没有更多的随机断言。 断言只能在代码的指定部分中。
- 测试代码更具可读性。
- 有关失败测试的更多信息
- 可以用更少的代码进行模拟和存根
- 可以利用大量的Groovy功能使代码不再那么冗长
- 非常强大的测试参数设置,可以非常整齐地完成
- 一系列功能强大的注释。
通常被遗忘的要点之一是您的项目不必用Groovy编写。 您可以将其全部保留在Java中,并为生产代码使用Java的静态类型,并为测试代码使用Groovy的功能和速度。
直到下一次照顾自己。
翻译自: https://www.javacodegeeks.com/2018/03/testing-code-spock.html